Building Better Javascript (Through Testing)
Last week I talked about our Javascript tool chain. One piece of our chain that we’ve invested quite a bit of time in so far, is testing. Our reason for spending so much time: making tests easy to write, run, and manage increases the odds they’ll actually get used. We looked at a few different unit testing libraries when we were getting started. Our requirements for a unit testing library were:
- able to be run from the browser and the command line
- cleanly written (to make extension and modification easier)
- not tied to a particular framework (didn’t depend on a specific JS library)
- low on boilerplate
The best fit that we found was QUnit. It’s the unit testing library used by jQuery. It’s a very cleanly written library that already has some hooks for integration with browser automation tools. QUnit has a very minimalistic interface. Knowing the functions ’test’, ’equals’, and ’ok’ is all you need to get started. Here’s an example:
test('point in middle of granular region', function() { var map = new up.common.BucketMap(10); var point = new up.common.Point(5, 4); var keys = map.locationKeys_(point); equal(keys.length, 1, "Only one key for point locations"); equal(keys[0], up.common.BucketMap.BucketKey_(0, 0)); });
QUnit on it’s own is great. But there are some very real points of friction that we found with our process and other tools. The big ones are:
- Managing dependencies with tests
- Dealing with overrides/ injecting mocks
- Keeping test HTML in sync with the JS
- Being able to run tests in one click/command
The first and second points come from our embracing Google’s Closure library. Being able to break up our application into proper namespaces and classes has been great for development. When you go to run a test however, you need to make sure that all of the right dependencies get loaded. The third issue crops up when you have tests that need to interact with the DOM. They may need certain elements or structures to be present to effectively test a unit of code. In most examples that we found a separate js and HTML get created. The HTML files is used to bootstrap the testing environment with the correct dependencies and DOM. The last issue is dealing with lowering the friction to test. If you have to type in 5 commands to run a test, chances are you’re not going to test very often. To solve all of these issues we’ve cooked up a QUnit/closure testing harness. It’s main features are:
- test’s are a single js + HTML + list of custom dependencies
- dependencies get dynamically loaded and overrides are used to allow for easy injection of mocks
- can run multiple tests in a row, cleaning the environment and dependencies in between
- has hooks for running from the command line as well as the browser
- the entire set of unit tests can be kicked off with a single ‘scons jstest’
It still has some issues when the tests contain syntax errors, and the command line version still blows up occasionally, but it’s helping to make the Upverter testing process smoother.