Mocking out the Request module in Node.js for Testing

Posted by Richard Lucas on Aug 10 2015

So, recently I needed to write a test that required that I mock the request module out as I wanted to pass a specific error code back to the response handler and do something different with it. In this case, I wanted to retry the request.

I first attacked the issue with Sinon.js library. If you’ve ever tested in javascript, sinon.js is an indispensable mocking and spying library.

So, I wrote:

1
2
3
4
5
6
7
8
9
10
11
...
var request = require('request'),
...
before(done) {
stub = sinon.stub(request)
.withArgs({
method: 'GET',
url: 'http://example.com'
});
done();
}

However, that doesn’t work as sinon.js requires if you’re stubbing out a module for there to be method that you use, i.e. stub = sinon.stub(request, 'get')see this StackOverflow issue

The problem was that the api that I was testing had already largely been written and I didn’t now want to rewrite a bunch of it just to make it testable for this one case. So, enter Mockery.

Mockery is great in this instance, but be careful when you use it as it tears down the require statement, so use it as tightly as you can and tear it down immediately after use. So, I separated out my test:

myModule.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
var request = require('request');
...
function handler(callback) {
return function(err, res, body) {
if (err) {
//doStuff
}
...//doOtherStuff
}
}
exports.getSomeStuff = function(url, callback) {
return request({
method: 'GET',
url: url
}, handler(callback));
};

test.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
var request = require('request');
...
describe('request error testing', function() {
var stub, module;
before(function(done) {
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
stub = sinon.stub();
mockery.registerMock('request', stub);
mm = require('./myModule');
done();
});
after(function(done) {
mockery.disable();
done();
});
it('should retry once on ETIMEDOUT error', function(done) {
var spy = sinon.spy(client, 'request');
var err = new Error('connect ETIMEDOUT');
stub.yields(err, null, null);
mm.getSomeStuff('http://example.com', function(err, json) {
expect(spy.calledOnce).to.be.true;
expect(stub.calledTwice).to.be.true;
expect(err.message).to.include('ETIMEDOUT');
done();
});
});
});

So, there you have it. Comment if you have any thoughts or questions.

Another really great post that helped me: http://bulkan-evcimen.com/using_mockery_to_mock_modules_nodejs.html

Cheers!

Richard