Testing with Mocha

08 Dec 2011 | By Alex Young | Tags node modules tutorials testing

Mocha (GitHub: visionmedia / mocha, npm: mocha, License: MIT) by TJ Holowaychuk is a new test library that does just about everything a JavaScript developer needs, and yet remains customisable enough to support both behaviour-driven and test-driven development styles.

Mocha presents itself as an unassuming and lightweight library, but it actually goes far beyond the test frameworks I’m familiar with in Node. I’ve written literally hundreds of tests for various Node apps, and the experience hasn’t always been positive. Even popular test libraries lack functionality that asynchronous, database-driven web apps require for sane testing. My recommendation is to take Mocha seriously and give it a try — it’s surprisingly intuitive.

The Basics

Mocha is available through npm, but I install it by adding it to my package.json files. Make a package.json file like this one:

{
    "name": "async-testing-tutorial"
  , "version": "0.0.1"
  , "description": "A tutorial for Mocha"
  , "keywords": ["test", "tutorial"]
  , "author": "Alex R. Young <info@dailyjs.com>"
  , "main": "index"
  , "engines": { "node": ">= 0.4.x < 0.7.0" }
  , "scripts": {
    "test": "make test"
  }
  , "devDependencies": {
    "mocha": "0.3.x"
  }
}

Then run npm install and you’re almost ready to use Mocha. The Mocha script will be in ./node_modules/.bin/mocha, so rather than typing this every time add a simple Makefile to your project:

test:
	@./node_modules/.bin/mocha

.PHONY: test

While you’re at it, add a test/ directory:

mkdir test

Now typing make test will run any tests in test/!

Any options for Mocha can now be added to the Makefile:

test:
	@./node_modules/.bin/mocha -u tdd

.PHONY: test

This says we want to run tests in the TDD style:

The “BDD” interface provides describe(), it(), before(), after(), beforeEach(), and afterEach(). The “TDD” interface provides suite(), test(), setup(), and teardown().

Testing Synchronously

We’d better write something! Add this to index.js:

function nextPrime(n) {
  var smaller;
  n = Math.floor(n);

  if (n >= 2) {
    smaller = 1;
    while (smaller * smaller <= n) {
      n++;
      smaller = 2;
      while ((n % smaller > 0) && (smaller * smaller <= n)) {
        smaller++;
      }
    }
    return n;
  } else {
    return 2;
  }
}

module.exports.nextPrime = nextPrime;

This is just an excuse to do something, it’s not meant to be the world’s greatest prime number generator. One reason I like this function is large numbers will make the interpreter really slow, which can be useful for demonstrating certain run-loop related issues.

Now create a new file called test/test.next-prime.js:

var assert = require('assert')
  , nextPrime = require('./../index').nextPrime
  , asyncPrime = require('./../index').asyncPrime;

suite('nextPrime', function() {
  test('nextPrime should return the next prime number', function() {
    assert.equal(11, nextPrime(7));
  });

  test('zero and one are not prime numbers', function() {
    assert.equal(2, nextPrime(0));
    assert.equal(2, nextPrime(1));
  });
});

And run make test in the terminal. The test should pass:

Why is this simple test interesting? Well, Mocha explicitly supports asynchronous testing, yet we didn’t have to tell it that the test had finished running. To do that, we’d write a test like this:

var assert = require('assert')
  , nextPrime = require('./../index').nextPrime;

suite('nextPrime', function() {
  test('nextPrime should return the next prime number', function(done) {
    assert.equal(11, nextPrime(7));

    // Tell Mocha the test has finished
    done();
  });
});

How is Mocha able to achieve this amazing feat of introspection? It’s actually just checking how many arguments the callback has by using the length property on Function. This makes the API able to cope with both synchronous and asynchronous tests without any extra boilerplate coding. If you want to read more about this, MDN: Function length has examples.

Asynchronous Tests

Expanding on nextPrime, I’ve added a contrived asynchronous version:

function nextPrime(n) {
  var smaller;
  n = Math.floor(n);

  if (n >= 2) {
    smaller = 1;
    while (smaller * smaller <= n) {
      n++;
      smaller = 2;
      while ((n % smaller > 0) && (smaller * smaller <= n)) {
        smaller++;
      }
    }
    return n;
  } else {
    return 2;
  }
}

function asyncPrime(n, fn) {
  setTimeout(function() {
    fn(nextPrime(n));
  }, 10);
}

module.exports.nextPrime = nextPrime;
module.exports.asyncPrime = asyncPrime;

The asyncPrime method just wraps the old function with setTimeout to simulate an asynchronous call. Now we need to use the done function provided by Mocha to signal the end of the test:

suite('asyncPrime', function() {
  test('asyncPrime should return the next prime number', function(done) {
    asyncPrime(128, function(n) {
      assert.equal(131, n, 'Wrong number');
      done();
    });
  });
});

Mocha Tricks

Mocha comes packed with lots of test reporter format options, including TAP and JSON, which provide opportunities for easy integration with other tools like Continuous Integration (CI) services. TJ uses Travis and has a little build status indicator on Mocha’s README page.

Mocha also works with CoffeeScript and web browsers.

Sample Project

If you need help getting started with Mocha, I’ve put the code for this tutorial on GitHub at alexyoung / async-testing-tutorial, and TJ’s projects use Mocha, which means popular libraries like Express have plenty of examples to explore.


blog comments powered by Disqus