Building Better Javascript (Through Testing)

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.

Our Javascript Toolchain

Our Javascript Toolchain

Javascript is a language that imposes very little on the developer. The web is littered with Javascript that looks like it was taken from Enterprise Javascript. To bring some sanity to our lives we’ve assembled a set of tools to help in our development efforts. The Javascript tool chain here at Upverter is comprised of:

  • customized qunit and testing harness
  • SConstruct build files for linting, compiling, unit tests, documentation generation

You’ll notice the list is quite Google heavy. When we were starting in we had a discussion about how much of the Closure coolaid we wanted to drink. There were a few concerns we had:

  • It could marry us to the compiler and library. If something better came along it could be painful to switch.
  • The overhead from annotating our code would drive us insane

It has some pretty big positive aspects going for it too:

  • Type annotations encourage documentation
  • Static analysis lets us shake bugs out faster and helps us refactor more confidently
  • There isn’t anything remotely comparable out there for static analysis and compilation

In the end the pros outweighed the cons and we dove in. It’s worked out well so far. Having those tools in place is helping us write better, faster code. (although gjslint’s whiny, inflexibility has resulted in more threatening of an inanimate object than is probably healthy) Lastly there are a couple of resources that I’d like to share in case you haven’t ran into them. Javascript can be a tough language to search for. These links are great starting points to find good answers:

Javascript Development

While building Upverter we have had to slog through mountains of Javascript. This has been an eye opening experience into what Douglas Crockford describes as “The World’s Most Misunderstood Programming Language”. Over the next couple of weeks we’re going to give you a glimpse into Javascript development at Upverter and share some of the things we’ve learned about:

  • IDE’s, code style, static analysis…
  • Javasrcipt testing
  • profiling and performance tuning
  • “building” a project
  • debugging