Be the first user to complete this post

  • 0
Add to List

Unit test your Nodejs RESTful API using mocha

Its always a good idea to have some quick and simple automated tests in place for your REST api so that you know that you didnt break anything that already worked when you add more functionality. Fortunately, setting up automated tests is not that hard in nodejs. You just gotta know the right libraries to use and how they work together. And thats exactly what we will set out to achieve in this article. All the source code for this tutorial is combined with the node starter kit project and can be found on github in the branch rest-api-unit-test This is what you would be able to do by the time we are done with this article. Run an npm script executes mocha tests against your nodejs RESTful api. Lets jump in.


Background

For the purpose of this article, I created tiny REST api for a /api/users. We are simply using an array to store the names of some users. You can replace it with any complex logic of your own api that is probably dealing with a backend system.
module.exports = function(app, settings){

    var url = require('url'),
        express = require('express'),
        usersRouter = express.Router();

    var users  = [
        {
            name: 'Tim Cook'
        },
        {
            name: 'Jony Ive'
        },
        {
            name: 'Steve Jobs'
        },
    ];

    // Get a list of all the users
    usersRouter.get('/', function(req, res, next){
        res.json(users);
    });

    // Get the user at the index
    usersRouter.get('/:index', function(req, res, next){
        res.json(users[req.params.index]);
    });

    // Create a new user
    usersRouter.post('/', function(req, res, next){
        users.push(req.body);
        res.json(users[users.length - 1]);
    });

    app.use('/api/users', usersRouter);

};

Packages

To prepare our project for unit testing, we will need to install 4 npm packages.
  1. mocha : The testing framework we will use.
  2. grunt-mocha-test : The task runner to help us setup mocha.
  3. chai : The assertion library that we will use within our mocha tests.
  4. supertest : A library that lets you fake requests to test your REST api's.
npm install mocha grunt-mocha-test chai supertest --save-dev

Configuration

package.json Create a script in your package.json as follows
scripts:{
    // other scripts
    // ...
    "tests-unit": "grunt mochaTest"
}
As you see, we just created an npm script as an alias for the grunt mochaTest command. You may already have many other scripts configured. For example, take a look at some of the other scripts that I have setup in my package.json and see if you find any of them to be useful. Now lets add the mochaTest grunt task to our Gruntfile. As usual, I will be following a lean Gruntfile strategy by keeping the task configurations in their own module under a directory that I call 'grunt' and then requiring them from my Gruntfile. Below is how my Gruntfile would look like if mochaTest was the only task configured. Look at my gruntfile to see how I have configured other tasks which follow the same pattern. Gruntfile.js
module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json')
    });

    grunt.config( 'mochaTest', require('./grunt/mochaTest.js') );

    grunt.loadNpmTasks('grunt-mocha-test');
}
mochaTest task configuration Now, lets configure our mochaTest task in grunt/mochaTest.js.
module.exports = {
  all: {
    options: {
      reporter: 'list'
    },
    src: ['tests/unit/**/*.test.js']
  }
};
You can see that we specified a reporter parameter that determines the output format of the console when we run the tests. Mocha has many other formats. I preferred to go with the 'list' reporter format for this project. The other important parameter is 'src', that as the name suggests is an array of the url patterns for the source files that will be tested. We will keep all of our test files in the tests/unit directory and give it an extension of '.test.js' so that even if the core names of the test files are same as any other file, opening them in an editor does not cause any confusion. That is all that you require for setting up the command to run our test. The next important thing to do is to write the test file itself.

The Test

There are couple of things that we will need to do in our test file. We need to require supertest to create a request object and require chai as our assertion library.
var request = require('supertest'),
    expect = require("chai").expect;
The request object that we just created allows us to create HTTP requests if we do one other thing - we have to create an app, aka - an instance of express and pass it to the request object. We will need to create this object almost exactly the way we do it in our app.js file. The only difference being that we dont need to require http, start the server or specify a port.
var express = require('express'),
    ROOT_DIR = __dirname + '/../..',
    config = require(ROOT_DIR + '/config'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    cookieParser = require('cookie-parser'),
    session = require('express-session'),
    app = express();

var settings = {
        config: config
    };

// all environments
app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());
app.use(methodOverride());
app.use(cookieParser());
app.use(session({
    secret: config.SESSION_SECRET ,
    saveUninitialized: true,
    resave: true
}));

//This allows you to require files relative to the root in any file
requireFromRoot = (function(root) {
    return function(resource) {
        return require(root+"/"+resource);
    }
})(ROOT_DIR);
We need to do this because we want our app to work just like our real application, the only difference being that its not a server. With the app object setup, we can now proceed with writing our mocha tests. In the mocha tests that follow, we do a couple of initialization tasks work before we invoke our REST api for testing.
  • We save a reference to the console.log. We do this because in my routes, I print logging statements to the console which will ruin the format of my mocha tests. So we save a reference to the log object and before each test executes, we will set it to an empty function(in the beforeEach function which runs before each 'it' block).
  • In the before function(which runs once at the beginning of the describe block) we require the route we intend to test in the test cases of this describe block.
  • In each test case, when we are ready to use our assertions, we again reference the console.log to the previously saved log variable.
The code that you see below has examples of testing your RESTful api using GET and POST.
describe('#/api/users', function(){

    var log = console.log;

    before(function(done){
        require(ROOT_DIR + '/routes/users')(app, settings);
        done();
    });

    beforeEach(function(){

        // Done to prevent any server side console logs from the routes
    // to appear on the console when running tests
        console.log=function(){};

    });

  it('- should GET users', function(done){
    request(app)
      .get('/api/users')
      .end(function(err, res){
        // Enable the console log to print the assertion output
        console.log = log;
        var data = JSON.parse(res.text);
        expect(err).to.be.null;
                expect(data.length).to.equal(3);
        done();
      });
  });

  it('- should GET a user at index (1)', function(done){
    request(app)
      .get('/api/users/1')
      .end(function(err, res){
        // Enable the console log
        console.log = log;
        var data = JSON.parse(res.text);
        expect(err).to.be.null;
                expect(data.name).to.equal('Jony Ive');
        done();
      });
  });

  it('- should POST a user and get back a response', function(done){
    var user = {
        name: 'Steve Wozniak'
    };

    request(app)
      .post('/api/users')
      .send(user)
      .end(function(err, res){
        // Enable the console log
        console.log = log;
        var data = JSON.parse(res.text);
        expect(err).to.be.null;
        expect(data.name).to.equal(user.name);
        done();
      });
  });

});
You can look at the complete test file here. Now when you run npm run test-unit from the terminal, you should see your tests pass if everything went well. Also, take a look at the github repos of supertest, chai bdd expect/should and the mocha website for any more configurations options, but I hope that what we have here should give you a good base to start from.



Also Read:

  1. Configuring jshint as a pre-commit hook for your nodejs applications
  2. Dynamic module loading with require
  3. What does npm start do in nodejs
  4. exports is not defined
  5. Understanding routers in express 4.0 - Visually