Testing With Mocha, Sinon.js & Mocking Request
Introduction
Writing tests with Mocha is really fun and satisfying. Combined with should.js
for expectations/assertions and mocking/stubbing with sinon.js
I think it becomes very powerful test environment.
In this article I will show you how to get started with test mocking/stubbing by showing an example which stubs out the get
method on mikeal/request. I also
assume you are somewhat familiar with Node.js
and have it installed.
Before we start create a directory and install the dependencies needed.
mkdir test_article; cd test_article
npm install mocha should sinon request async
GET some data
Here is a function that will get any users public GitHub profile from using the GitHub API. We will use the async
module to help with the asyncronousness.
var request = require('request')
, async = require('async');
function getProfile(username, cb){
async.waterfall([
function(callback){
request.get('https://api.github.com/users/' + username, function(err, response, body){
if (err) return callback(err);
callback(null, body);
});
}
], cb)
}
module.exports = getProfile;
getProfile('bulkan', function(result){
console.log(result);
});
We require
the two packages we need, then define a function which accepts the GitHub username & a callback function.
async.waterfall
takes an array of functions as its argument then calls them one by one passing the values from that is passed to the callback
to the next function. For more details read the official description here.
The first function in async.waterfall
function list does a request to GitHub API passing the body
to the next function. We dont have have another function so async.waterfall
will call the callback we passed intogetProfile
with err and result as its argument. Its a good idea to read the official description of the waterfall
function.
Last, we export our function as the module so we can use it later.
Tests taste better with Mocha
To test the above code we can write a Mocha test assuming the above code is in a file named gh.js
.
var getProfile = require('./gh');
describe('User Profile', function(){
it('can get user profile', function(done){
getProfile('bulkan', function(err, result){
if(err) return done(err);
// simple should.js assertion
result.should.not.be.empty;
done();
});
});
});
We can run the above test via the following line assuming the Mocha test is in a file named gh_test.js
./node_modules/.bin/mocha --require should --ui bdd gh_test.js
The problem with this test is that each time we run it will do a HTTP GET to GitHub API which will be a slow. The more tests we add that do actual
HTTP requests to third parties will slow the tests even more. What we can do is we can mock out the request.
We can change the test code.
var request = require('request')
, sinon = require('sinon')
, getProfile = require('./gh');
describe('User Profile', function(){
before(function(){
sinon
.stub(request, 'get')
.yields(null, null, JSON.stringify({login: "bulkan"}));
});
after(function(){
request.get.restore();
});
it('can get user profile', function(done){
getProfile('bulkan', function(err, result){
if(err) return done(err);
request.get.called.should.be.equal(true);
result.should.not.be.empty;
done();
});
});
});
We add a before
call that stubs out request.get
.
The yields
allows us to simulate the call to the callback that is passed to request.get
. In this case
we return null
for err, null
for the response and JSON string of a simple object.
The after call restores the default request.get
method.
Our test case tests that request.get
was called.
In Node.js require(..)
loads modules once into a cache. As our test case runs first it will load the request
module first. We use the
reference to the module to be able to stub methods on it. Later on when we load getProfile
module it will do a require('request')
call which will retrieve the module from the cache with
the get
method stubbed out.
I hope this example helps you in your testing.