Skip to content

cjohansen.no

Unit testing node.js apps

Recently I've been dabbling in the fantastic library that is node.js. Whenever I enter a new development environment I immediately poke around to figure out how to unit test things. I'm uncomfortable writing too much code without tests, especially when I'm on unfamiliar grounds. Here's what I landed on in round one.

What node.js offers

In case you haven't already checked it out, node.js is "Evented I/O for V8 JavaScript" - a small framework for server-side JavaScript in a completely asynchronous fashion. node.js is already somewhat on par with CommonJS, implementing its Modules.

Node's focus is providing the low-level stuff, such as a file system module, TCP and HTTP modules and more. Relevant to testing, it also provides an Assert module. The module is very low-level, it simply provides an assert.fail method along with a few assertions:

These are fine building blocks to build a test library on, but not exactly a whole lot to test an application with. For example, there is no test runner provided, so no structured output, no way to group tests and so on. I needed more.

Nodeunit

The node.js GitHub wiki has a list of modules which is very cool way to discover the world of node.js. I started perusing some of the alternatives on this page. Of the available testing tools, I guess JsSpec, which also works in browser, is one of the more accomplished ones, however I'm not too keen on the syntax.

I landed on a small project called Nodeunit, which is mostly modeled after the QUnit API. It's very minimalistic, too minimalistic for my taste, but I liked its way of organizing stuff.

How to use nodeunit

In nodeunit, tests can simply be exported from modules. Every test method receives a test context object from nodeunit, which has the assertions and some state associated with the tests. Nodeunit is, as node.js itself, completely asynchronous. It simply assumes your tests will be asynchronous, and expects you to tell it when you're done running tests by calling the done method on the test context object.

The following is a slightly adjusted version of the example from nodeunit's GitHub repo:

exports["test some stuff"] = function(test){
    test.expect(1);
    test.ok(true, "this assertion should pass");
    test.done();
};

exports["test some other stuff"] = function(test){
    test.ok(false, "this assertion should fail");
    test.done();
};

Everything you need for testing inherently asynchronous code; a way to expect the number of asserts, and explicit test completion.

Running tests

One of the things that sold me on nodeunit was the very useful Readme Caolan provides with the code. He gives good pointers on how to run the tests, and suggests this script:

#!/usr/local/bin/node

require.paths.push(__dirname);
require.paths.push(__dirname + '/deps');
require.paths.push(__dirname + '/lib');
var testrunner = require('nodeunit').testrunner;

process.chdir(__dirname);
testrunner.run(['test']);

Assuming you have dependencies in deps, library code in lib/ and tests in test, this will conveniently run all your tests. Save it in e.g. run_tests and make the file executable and you're good to go.

Autotesting

I love automatically running tests. For client side JavaScript, I've been using my JstdUtil tool, and for Ruby I've been using Watchr for quite some time (actually jstd util uses Watchr under the hood as well). Watchr can easily be used to run nodeunit tests automatically too. Save the following snippet in a file named autotest.watchr in the root of your project:

def run_all_tests
  print `clear`
  puts "Tests run #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
  puts `./run_tests`
end

run_all_tests
watch("(test|lib)(/.*)+.js") { |m| run_all_tests }

@interrupted = false

# Ctrl-C
Signal.trap "INT" do
  if @interrupted
    abort("\n")
  else
    puts "Interrupt a second time to quit"
    @interrupted = true
    Kernel.sleep 1.5

    run_all_tests
    @interrupted = false
  end
end

The run it with watchr autotest.watchr, and swoosh! Tests run automatically every time you save a file in the project. Now we're getting somewhere, but I still wanted more.

Assertions

I have previously implemented a bunch of assertions for JsTestDriver. I love high-level assertions, they make tests more concise, easier to read and less error-prone. So I went ahead and added the following assertions for node.js as well:

The assertions live in the assert-extras modules, and you can use the with the regluar asserts like so:

function mixin (target) {
    Array.prototype.slice.call(arguments, 1).forEach(function (object) {
        Object.keys(object).forEach(function (property) {
            target[property] = object[property];
        });
    });

    return target;
}

var assert = mixin({}, require("assert"), require("assert-extras").assert);

Now both the built-in assertions and the custom ones appear to live in the same module when using it.

Test cases

The last piece of the puzzle was a way to group tests in test cases. Caolan argues on nodeunit's GitHub repo that setUp methods can be easily handled manually, and while that's true I think the proposed solution is less clear than desirable in a test case, so I wanted my regular setUp and tearDown methods. To scratch my own itch I forked nodeunit and added test cases. They can be used like so:

testCase(exports, "some method", {
    setUp: function () {
        this.someVal = 42;
    },

    tearDown: function () {
        // teardown code
    },

    "should do something": function (test) {
        test.isFunction(some.method);
        test.equals(some.val, this.someVal);
        test.done();
    }
});

You can add several test cases with each their sets of setUp and tearDown methods in a module. The testCase method is nothing fancy, it simply prepends the test case name to the names of all the tests, and takes care to run setUp and tearDown for all tests.

That's what I have so far. Next up I imagine some way to more easily stub promises will be useful, but I need some more time developing and testing node applications before I know what I want.