Test-Driven JavaScript

Aug 15

Our site is mainly a one page site which makes it very javascript heavy. Up until recently, even though the rest of our stack has great test coverage, our javascript had no automatic test coverage. We recently did a huge redesign of our site and, in doing so, decided to rewrite a lot of the javascript. We committed to write all of it test-driven like the rest of our code. 

This was the first go at test-driven javascript for both Ben and I. It worked out really well. We ended up with some great reusable components which has made adding new features, like groups, a lot faster. I figured it’d be worth sharing how we did it and a couple of tips we learned.

The Tool Set

Setup

On our web frontend, our javascript files are all stored in the directory public/javascripts. We also created a directory called js-tests where we keep our javascript tests. For each file in public/javascripts there is a corresponding file in js-tests with the same name.

Even if your production site is linking to an external source for it’s jQuery, I suggest having a local version for your tests so that you can work without an internet connection.

For our example we will have the simple inheritance library (called resig.js) in the public/javascripts directory, and js-tests will contain jquery.js, qunit.js and qunit.css. Finally, we need one file which will actually run our tests, which will also be in js-tests, and simply called index.html.

Our initial index.html would look something like this:

Create our first test

Ok, now to actually setup our first test. Let’s assume for this example we want to create a class that, when instantiated, will cause a button on the screen with the id of go-banana, when clicked, to do a get request to /bananas, and populate a drop-down using data from the json returned from the endpoint.

Normally I’m a subscriber to Kent Beck’s belief that you should take really small steps when writing code test-driven, but in the interest of brevity, I’m going to skip ahead a bit. Based on the use case, we know that our class is going to have to make an ajax request. We don’t want our javascript unit tests actually calling out to anything, so we are going to take control of this. The way to do that is to have a dependency injection point in our class where we can inject in when the class is trying to call out to the endpoint. We ended up using this pattern in almost of our javascript classes in Attachments.me. 

It looks something like this:

Notice how, by default, we will have this.ajax available to us, and it will just call out to jQuery’s standard $.ajax. However, since we allow the parameters passed into the constructor to override the defaults, we can override what this.ajax does. Now we’re ready to write our first test.

It looks like this:

We create a fakeAjax function where we have two expectations. We expect the ajax function to be called with the url /bananas and the type get

Next we add in both the production class and the test to the index.html:

If we point our browser to the index.html file, we should now have a red bar and a failing test. Now we can go back to the BananaPopulator class and make the test pass. It ends up looking like this:

Boom, we’ve got a green bar and a passing test.

Fake out the endpoint

Now we’ve got a test that confirms that we are actually calling out to the endpoint properly. Next we need to fake out the end point returning something so we can confirm our class does the right thing with the data returned. So, now we need to not only override the ajax function, but cause it to act as if data is being returned.

First, in index.html, we need to add a select element that our class can interact with. Something like this:

<select id="banana-selector"></select>

Then we can write our test, which ends up looking like this:

As you can see, we call the success function from the parameters passed into the ajax method and pass in our fake json struct. We are then asserting that there are two options added into our select element. Refreshing our test page, we should now be back to having a failing test.

Going back to our class, we can make the test pass by making it look like this:

And we’re back to green! So we now have a fully tested class for calling out to an endpoint and populating a selector. This is obviously a simplified example but I hope you can see that using the dependency injection technique we used here can allow you to fake out a lot of behaviour and really test what your class is supposed to do. We also found that it drove our designs to be much less coupled to the DOM and made for some great reusable code.

I’ve posted the complete code for this example on our GitHub page here: https://github.com/attachmentsme/tdd_javascript_example

As always you can reach out to me with any questions.

— Jesse (@jesse_miller)


  1. sido reblogged this from attachmentsme and added:
    Nice introduction...Test-driven development (TDD)
  2. kanazawajs reblogged this from attachmentsme
  3. ethanwinters reblogged this from attachmentsme
  4. attachmentsme posted this