Got a large JavaScript client-side codebase you’re looking to test, but aren’t sure where to start? 9 months ago, so were we. We found there are lots of tools out there, but less information about setups and approaches for testing. This post will cover what we learnt, what our test setup looks like, and some common patterns our tests use.

The case for client side testing

We recently finished the first phase of the National Reporting Toolkit, a site which has a substantial client-side javascript component. How substantial? This is what it our report building interface looks like with JavaScript disabled.

Clearly, it is just as important that our client-side code works as our server-side code. We also knew that with a client-side codebase that big, we’d need tools to allow us to confidently refactor in response to changing requirements. In other words, we wanted good test coverage.

What *kind* of testing?

While we knew we needed coverage, we first had to decide what kind of testing we were going to do. In the past, we had experimented with acceptance tests, using cucumber, capybara and selenium. These would cover our client-side javascripts, but are slow to run, fragile and hard to maintain. We’re also believers in TDD for driving good application design, and the slow feedback loops of these tools make them unsuitable for this purpose.

So instead, we decided to unit test our client side code. Here’s our unit testing toolset:

mocha.js - Mocha is one of the most popular JS unit testing frameworks. We’re using it with the ‘qunit’ style syntax (I talked a little bit about why I’m not keen on specs here).

- Mocha is one of the most popular JS unit testing frameworks. We’re using it with the ‘qunit’ style syntax (I talked a little bit about why I’m not keen on specs here). chai.js - a lightweight, expressive assertion library.

- a lightweight, expressive assertion library. sinon.js - for stub and mocking (more on that later).

The setup

We decided the best place to run our browser based tests was in the browser. We created a /test route in our application, which is only accessible in the development environment:

This loads up our libraries, our application code and our tests and runs them all in the browser. This also has the benefit that you can do (some) cross-browser testing by running the tests in different browsers.

At this point, I’d also note how incredibly fast these tests run. Our suite of ~180 unit tests runs in Safari in less than a second. If you’re coming from a rails testing background, this is a pleasant surprise :-)

Some testing patterns

So, what do our tests look like? Here are some patterns we find ourselves using a lot. These are loosely based around some Backbone.js MVC concepts, but should transfer across frameworks fairly easily.

Basic unit test

Not much to see here. Create an object, run a function, check it returns what you expect.

View rendering test

Here we’re checking that a basic view renders the given data correctly. `view.$el` points to a jQuery DOM element, which we check the text of.

View event binding test

Here we’re testing that a view responds correctly to an event from one of it’s DOM elements. A sinon.js spy checks to see that the listener function is called.

Avoiding hitting the server v1 - stubs

Hitting the server in your client side tests is pretty much a no-no. It creates a dependency on having your application server running, and creating and destroying context in your persistent storage on the server is loads of work.

Here we’re testing a function that would call `save` on our model, triggering an ajax request. To prevent this, we use sinon.js to stub the save function out:

Avoiding hitting the server v2 - faking a server

Where possible, the above approach of stubbing server querying methods out is the easiest way to avoid the server. However, sometimes it isn’t possible, like in this example, where we’re testing the method responsible for performing an AJAX request.

In this example, we use sinon.js to create a fake server, which all AJAX requests will hit instead. We then use this fake server to provide the responses we’re expecting.

Important note from the above: Whenever stubbing something which outlives the context of your test, it’s important to make sure the stub restores occur in a try{} finally{} block, to prevent your test stubs leaking into the next tests.

Conclusion

We’ve found this testing setup to be a great success. It has given us the the confience we lacked in previous client-heavy applications, and has allowed us to refactor with an aggression we wouldn’t have managed without it.

Hopefully these testing patterns will help you if you want to test your client side applications. The National Reporting Toolkit is open source, so you can also go browse our examples there:

https://github.com/unepwcmc/NRT/tree/master/client/test/src

Enjoyed this post? Why not follow me on twitter