DailyJS

DailyJS

The JavaScript blog.


Tagmocha
Featured

libraries testing node mocha

Assertion Counting in Mocha

Posted on .

A few weeks ago I wrote about node-tap, in Why Don't You Use Tap?. I usually use Mocha for my tests, and one thing I liked about node-tap was the idea of test plans:

test('Check out my plan', function(t) {  
  t.plan(1);
  t.ok(true, "It's ok to plan, and also end.  Watch.");
  t.end();
});

Test plans help in situations where you want to put assertions inside asynchronous events. For example, if you're testing a web application and it makes HTTP requests in the test cases, but you also intercept events that indicate various lifecycle events. These could be things like ensuring a notification email was sent when a user signs up, and also checking that their account was saved to the database.

In Mocha, such a test might look like this:

describe('Account creation', function() {  
  it('should allow users to sign up', function(done) {
    app.on('notify:accounts:create', function(account) {
      assert(account, 'expected a user account object');
      done();
    });

    request(app).post('/accounts').send(userDetails).expect(200, done);
  });
});

That's OK, but it has some problems: done will be called twice -- once when the web request finishes, and again when the email event is sent (notify:accounts:create).

We could fix this by counting how many assertions have been called:

describe('Account creation', function() {  
  it('should allow users to sign up', function(done) {
    var expected = 2;

    function checkDone() {
      expected--;
      if (expected === 0) {
        done();
      }
    }

    app.on('notify:accounts:create', function(account) {
      assert(account, 'expected a user account object');
      checkDone();
    });

    request(app).post('/accounts').send(userDetails).expect(200, checkDone);
  });
});

Seeing checkDone all over my tests made me create something more generic. In the following example I use instances of an object called Plan that allows assertions to be counted, and done to only get called once the specified number of assertions have passed. This example can be run with the mocha command-line script.

var assert = require('assert');

function Plan(count, done) {  
  this.done = done;
  this.count = count;
}

Plan.prototype.ok = function(expression) {  
  assert(expression);

  if (this.count === 0) {
    assert(false, 'Too many assertions called');
  } else {
    this.count--;
  }

  if (this.count === 0) {
    this.done();
  }
};

describe('Asynchronous example', function() {  
  it('should run two asynchronous methods', function(done) {
    var plan = new Plan(2, done);

    setTimeout(function() {
      plan.ok(true);
    }, 50);

    setTimeout(function() {
      plan.ok(true);
    }, 25);
  });
});

This code could be expanded to tie in with Mocha's timeouts to display the number of missed assertions, if any. Otherwise it does the job: if any of the asynchronous callbacks don't fire, Mocha will raise a timeout error. It also protects against calling assertions too many times, which can actually happen if you're making your own asynchronous APIs: I've had cases where I've triggered callbacks twice by mistake. And, it ensures done is only called when needed.

The question of ensuring assertions were actually called was brought up in this issue for Chai.js: Asserting that assertions were made. And, the Mocha wiki has this assertion counting snippet: Assertion counting.

Outside of Mocha, I found QUnit has asyncTest which allows assertions to be planned like TAP-style tests. With this approach we don't need to broker calls to done because it uses start instead.

I've never quite found the perfect solution to this problem, however. How do you ensure assertions are triggered in Mocha, and how do you handle calling done in tests where there are multiple asynchronous operations?

Featured

testing node modules websockets mocha crawling

Node Roundup: 0.8.14 and 0.9.3, Orange Mocha Frappuccino, Crawlme, WebSocketIPC

Posted on .

You can send in your Node projects for review through our contact form or @dailyjs.

0.8.14 and 0.9.3

Last week, 0.8.14 and 0.9.3 were released. The 0.8.14 release is basically the 0.8.13 release with a fix for EventEmitter:

Note: v0.8.13 contains a regression in the EventEmitter class. This is a bugfix release, but contains no new features. Most of the release notes are copied from v0.8.13, since it didn't live long.

The 0.9 release updates V8 and has various bug fixes. It also adds an options argument to util.inspect -- the older API had three arguments, so reducing the arity makes sense as new options can be supported more easily.

Orange Mocha Frappuccino

Orange Mocha Frappuccino (License: MIT, npm: omf) by Brian Carlson is a library to help build HTTP verification tests with Mocha. It can accept a Node HTTP server or URLs:

var omf = require('omf');  
var assert = require('assert');

omf('https://github.com', function(client) {  
  client.get('/brianc/node-omf', function(response){
    response.has.statusCode(200);
    response.has.body('ORANGE MOCHA FRAPPUCCINO');

    //the full response is available to any custom tests:
    it('has ETag header', function() {
      assert(this.response.headers['etag']).ok();
    });
  });
});

This is a little bit more like how Expresso worked, which was TJ's pre-Mocha test framework.

Crawlme

Crawlme (License: MIT, npm: crawlme) from Optimal Bits is Connect middleware for automatically mapping hashbang URLs to parameter-based URLs for Googlebot Ajax Crawling.

WebSocketIPC

WebSocketIPC (License: GPL) by Nicolas Froidure is a proof-of-concept of his VarStream "variable exchange format". This format is designed to be human readable, streamable, and self-referencial.

WebSocketIPC uses WebSockets to synchronise a variable tree between multiple clients and a server. It can also synchronise data between multiple Node instances by piping VarStreams.

It's interesting that as interest in using streams grows more diverse projects are appearing to better exploit their properties.

Featured

testing deployment node modules mocha

Node Roundup: 0.6.14, Mocha 1.0, ncluster, ENVy

Posted on .

You can send in your Node projects for review through our contact form or @dailyjs.

Node 0.6.14

Node 0.6.14 was released soon after 0.6.13. This release includes bug fixes and updates npm to 1.1.12.

Mocha 1.0

Mocha 1.0 has been released. There's now a GitHub-flavoured Markdown reporter, and JavaScript "compilers" are now supported with mocha --compilers coffee:coffee-script.

An API has also been added, so it's possible to programatically create tests:

var mocha = new Mocha;  
mocha.reporter('spec').ui('bdd');

mocha.addFile('test/suite.js');  
mocha.addFile('test/runner.js');  
mocha.addFile('test/runnable.js');  

ncluster

ncluster (npm: ncluster) by Ben Murphy is a zero-downtime clustering solution. It'll reload the application when a SIGHUP is issued, and gracefully quit when SIGQUIT is sent.

Ben has designed it to be used with Capistrano. He's also created a Node/Ubuntu Vagrant deployment solution with instructions for deploying his demo app to a virtual machine (nodejsvagranthelloworld).

ENVy

ENVy (npm: envy) by Elio Capella Sánchez is a small module for managing JSON settings files. It'll use require to load a file called config.json in the project's root directory.

It also uses a convention for automatically determining the environment and loading the appropriate properties. Given a JSON file like this:

{
  "environment": "development",

  "development": {
    "test": "Development property"
  },

  "production": {
    "test": "Production property"
  }
}

Then require('envy').config will load the development settings, so config.test will return "Development property".