Despite all my efforts to try to encapsulate some of my code into my modules, I was always founding myself exposing too much just for unit testing purpose. And even thought, it was often very complex to mock out some of the external libraries I was using. That was before I found the rewire library. As they say, it only: "adds a special setter and getter to modules so you can modify their behaviour for better unit testing".
This is all you need!
Let's see an example
I have a very simple controller like this:
'use strict';
var userService = require('../service/user-service');
exports.allUsers = function (req, res, next) {
userService.getUsers(function (err, users) {
if (err) {
return next(err);
}
res.json(users);
});
};
In my test driven development all I want to test is:
- this method will call
getUsers
onuserService
- if the callback receive an error then it will call
next
passing the error - else
json
is called on the response with the users as arguments
Setting up a mock is easy (I use sinon but any mocking will do it):
// given
var userController = require('./user-controller'),
userServiceMock = {};
// and
userServiceMock.getUser = sinon.stub().callsArgWith(1, null, [{id: 'user1'}, {id: 'user2'}]);
// when
userController.allUsers();
// then
userServiceMock.getUser.calledOnce.should.equal(true);
But how can I inject the mock instead of the real user service ?
=>Rewire!
Instead of require the user-controller I'm using rewire:
var userController = rewire('./user-controller')
Then I can get and set any properties on the module. So I can do:
before(function(){
userController.__set__({
'userService': userServiceMock
});
});
Et voila!
Here is the full test for the previous controller:
'use strict';
var sinon = require('sinon'),
rewire = require('rewire'),
userController = rewire('./user-controller');
describe('allUsers', function() {
it ('should return all users',function() {
// given
var userServiceMock = {},
res = { json: sinon.spy()},
users = [{id: 'user1'}, {id: 'user2'}],
nextStub = sinon.stub();
// and
userServiceMock.getUser = sinon.stub().callsArgWith(1, null, users);
// when
userController.allUsers({}, res, nextStub);
// then
res.json.calledWith(users).should.equal(true);
nextStub.called.should.equal(false);
});
});