Sorry for the confusing title, I have no idea how to better describe it. Let's see the code:
var client = require('some-external-lib').createClient('config string');
//constructor
function MyClass(){
}
MyClass.prototype.doSomething = function(a,b){
client.doWork(a+b);
}
MyClass.prototype.doSomethingElse = function(c,d){
client.doWork(c*d);
}
module.exports = new MyClass();
Test:
var sinon = require('sinon');
var MyClass = requre('./myclass');
var client = require('some-external-lib').createClient('config string');
describe('doSomething method', function() {
it('should call client.doWork()',function(){
var stub = sinon.stub(client,'doWork');
MyClass.doSomething();
assert(stub.calledOnce); //not working! returns false
})
})
I could get it working if .createClient('xxx') is called inside each method instead, where I stub client with:
var client = require('some-external-lib');
sinon.stub(client, 'createClient').returns({doWork:function(){})
But it feels wrong to init the client everytime the method each being called.
Is there a better way to unit test code above?
NEW: I have created a minimal working demo to demonstrate what I mean: https://github.com/markni/Stackoverflow30825202 (Simply npm install && npm test
, watch the test fail.) This question seeks a solution make the test pass without changing main code.
The problem arises at the place of test definition. The fact is that in Node.js it is rather difficult to do a dependency injection. While researching it in regard of your answer I came across an interesting article where DI is implemented via a custom
loadmodule
function. It is a rather sophisticated solution, but maybe eventually you will come to it so I think it is worth mentioning. Besides DI it gives a benefit of access to private variables and functions of the tested module.To solve the direct problem described in your question you can stub the client creation method of the
some-external-lib
module.However, if your test gets more complicated and the mocked client gets a state you will have to manually take care of resetting the state between different unit tests. Your tests should be independent of the order they are launched in. That is the most important reason to reset everything in
beforeEach
section