Be the first user to complete this post
|
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.- mocha : The testing framework we will use.
- grunt-mocha-test : The task runner to help us setup mocha.
- chai : The assertion library that we will use within our mocha tests.
- 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 followsscripts:{
// other scripts
// ...
"tests-unit": "grunt mochaTest"
}
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 requiresupertest
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 savedlog
variable.
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:
- What is npm shrinkwrap and when is it needed
- set the default node version using nvm
- Configuring jshint as a pre-commit hook for your nodejs applications
- Find the environment variables of a nodejs process in linux