I do a lot of work with node.js, and since the beginning I wrote my tests with Jasmine. Testing synchronous code has been extremely painless, like:

it('should test something', function() {
  expect(1).toEqual(1);
});

Most of my node code is written with callbacks, and I started to notice that using Jasmine, my async tests got pretty wild. A quick run-through will help explain how Jasmine is used, and why it works the way it does.

Testing asynchronous code is rough because you’d like to test expectations after some thing completes. Knowing when it completed is not directly possible in Javascript, so first attempts to test asynchronous Javascript led to relying heavily on timeouts:

var hitThatCallback = function(callback) {
  setTimeout(function() {
    callback('thing');
  }, 1000);
};

it('should test something', function() {
  hitThatCallback(function(thing) {
    expect(thing).toBe('funny');
  });
  waits(500);
});

This approach lasted for a bit (and can still be used in Jasmine), but as programmers it just didn’t feel right there just had to be a better way. The next step Jasmine took to keep track of those callbacks involved two methods called runs and waitsFor, which share functional scope with each other. Here’s how you would write the above test using them:

it('should test something', function() {
  runs(function() {
    var $this = this;
    hitThatCallback(function(thing) {
      $this.done = true;
      $this.value = thing;
    });
  });
  waitsFor(function() {
    return $this.done;
  });
  runs(function() {
    expect($this.value).toBe('funny');
  });
});

If you don’t think that’s out of control, you can use your imagination to see how it looks with multiple asynchronous calls after one another, but it definitely works exactly as we’d hope.

Mocha is another framework, and mine of choice recently. It takes a different approach to testing async:

it('should test something', function(done) {
  hitThatCallback(function(thing) {
    thing.should.equal('funny');
    done();
  });
});

By passing a function (done) into the test, you can track the end of the callback. If it hasn’t come back done or failed an assertion in a certain time (default 2000 ms), the test fails.

This definitely feels much more natural, and the same mechanism is available in beforeEach, afterEach, etc.

Mocha supports a lot of different reporter styles, several paradigms including suites (TDD) and describe/it (BDD) styles, and has a pluggable assertion system (so you can use should.js, chaijs, etc).

Overall, definitely a big improvement to the clarity of my test suites. Hope you like it too!