Wednesday, May 23, 2012

A Simple Introduction to Behavior Driven Development in NodeJS with Mocha

I recently had a chance to use the Mocha test framework for NodeJS on a large-ish project and wanted to provide a really simple introduction to it.  Mocha is a unit testing framework for NodeJS apps from TJ Holowaychuk.  As the successor to the popular Expresso test framework, it adds a number of new features. It supports both Test-Driven and Behavior-Driven development styles and can report test results in a variety of formats.

Mocha can be installed easily using npm. Assuming you have node and npm installed, you can install mocha as a global module using the -g flag to it will be available everywhere on the system.

npm -g install mocha

or perhaps

sudo npm -g install mocha

or, if you prefer a local install instead of a global install

npm install mocha
export PATH=./node_modules/.bin:$PATH

You're also going to want should.js, which you can install in your local project folder.

npm install should

Mocha, in addition to being a test framework, is a command-line utility that you run when you want to test things. You should be able to type `mocha -h` on the command line now and get some usage info.

Example


Before we can use mocha to test stuff we should have a little bit of code to test. I'm going to borrow from Attila Domokos' blog http://www.adomokos.com/2012/01/javascript-testing-with-mocha.html but try to simplify it just a bit. We're going to create two files in two different directories:

src/dog.js
test/dog_test.js

dog.js is our source code that we want to test.  Normally this would be a module that you have written. Since this is a super-simple introduction, it just exports one function called "speak". We can use this to invoke the "speak" method, which will return "woof!".

// dog.js
function speak() {
    return 'woof!';
}
exports.speak = speak;

Now we can write something to test our code: dog_test.js. Remember this file goes under the test/ directory and dog.js is under the src/ directory. So here is how we would write a test case for Dog.

// dog_test.js
var should = require('should'); 
var dog = require(__dirname + "/../src/dog");

describe('dog', function() {
  it('should be able to speak', function() {
    var doggysound = dog.speak();
    doggysound.should.equal('woof!');
  });
});

Now there should be a src/ and a test/ directory with these two files in them.

src/dog.js
test/dog_test.js


Mocha will automatically run every .js file under the test/ directory. To run the tests you can simple type 'mocha' on the command line:

mocha

and it should give you the following output:

  ✔ 1 test complete (3ms)

or if you want a little more detail use:

mocha -R list

and it should give you this output:

  ✓ Dog should be able to speak: 0ms
  ✔ 1 test complete (2ms)

Mocha can generate several different styles of test reports. To see a list of the types of reports it can generate, type mocha --reporters.


Explanation


Let's take a quick look at the test case definition. A group of tests starts with "describe". You provide some title for the thing you're testing. You can choose whatever title you want, but it should be descriptive. Our test suite is called 'dog'.

describe("dog", function () {
    // your test cases go in here
}

You can describe more than one group of tests in the file.

describe("dog", function () {
    // your doggy tests
}

describe("puppy", function () {
    // your puppy tests
}

Test descriptions can be nested, which is often convenient.

describe("dog", function () {
    describe("old tricks", function () {
        //  your old doggy tricks
    }
    describe("new tricks", function () {
        // your new doggy tricks
    }
}

Individual tests themselves start out like "it('should do such-and-so...". You can give the test whatever description you want, but generally you give it a brief explanation of what it should or shouldn't do. If you added more methods to Dog, you would add more tests within the "Dog" test description to test them.

describe("dog", function () {
    it('should be able to speak', function () {
        // the body of the speak test goes here
    }
    it('should be able to wag its tail', function () {
        // the body of the wag test goes here
    }
}

Our examples use should.js for writing test cases in Behavior Driven Development style. Should.js provides a bunch of cool assertions like foo.should.be.above(10) or baz.should.include('bar'), and so on. Check out the documentation for should.js here: https://github.com/visionmedia/should.js

    it('should be able to speak', function () {
        var doggysound = dog.speak();
        doggysound.should.equal('woof!');
    }
    it('should be able to wag its tail', function () {
        var wags = dog.wag(5);
        wags.should.be.above(4)
    }

Synch and Asynch Tests


These examples show an asynchronous testing approach. It's quite easy to run your tests in a synchronous way, though, by using the "done" callback which Mocha provides.


    it('should be able to speak', function (done) {
        var doggysound = dog.speak();
        doggysound.should.equal('woof!');
        done();
    }


Using the "done" callback means the test will complete before moving on to the next one.

For a slightly more advanced tutorial check out http://dailyjs.com/2011/12/08/mocha/. It has some great tips about setting up a package.json and Makefile to help with test automation, as well as writing synchronous and asynchronous tests.

No comments:

Productivity and Note-taking

I told a friend of mine that I wasn't really happy with the amount of time that gets taken up by Slack and "communication and sched...