I'm learning to unit test using the node module mockery with sinon.
Using only mockery and a plain class I'm able to inject a mock successfully. However I would like to inject a sinon stub instead of a plain class but I'm having a lot of troubles with this.
The class I am trying to mock:
function LdapAuth(options) {}
// The function that I want to mock.
LdapAuth.prototype.authenticate = function (username, password, callback) {}
And here is the code I'm currently using in my beforeEach() function:
beforeEach(function() {
ldapAuthMock = sinon.stub(LdapAuth.prototype, "authenticate", function(username, password, callback) {});
mockery.registerMock('ldapauth-fork', ldapAuthMock);
mockery.enable();
});
afterEach(function () {
ldapAuthMock.restore();
mockery.disable();
});
I've tried to mock/stub the LdapAuth class in various ways without success and the code above is just the latest version that doesn't work.
So I just want to know how to mock this successfully using sinon and mockery.
These node mocking libraries can be quite cumbersome because of Node's module cache, Javascript's dynamic nature and it's prototypical inheritance.
Fortunately Sinon also takes care of modifying the object you are trying to mock as well as providing a nice API to construct spys, subs and mocks.
Here is a small example of how I would stub the authenticate method:
// ldap-auth.js
function LdapAuth(options) {
}
LdapAuth.prototype.authenticate = function (username, password, callback) {
callback(null, 'original');
}
module.exports = LdapAuth;
// test.js
var sinon = require('sinon');
var assert = require('assert');
var LdapAuth = require('./ldap-auth');
describe('LdapAuth#authenticate(..)', function () {
beforeEach(function() {
this.authenticateStub = sinon
// Replace the authenticate function
.stub(LdapAuth.prototype, 'authenticate')
// Make it invoke the callback with (null, 'stub')
.yields(null, 'stub');
});
it('should invoke the stubbed function', function (done) {
var ldap = new LdapAuth();
ldap.authenticate('user', 'pass', function (error, value) {
assert.ifError(error);
// Make sure the "returned" value is from our stub function
assert.equal(value, 'stub');
// Call done because we are testing an asynchronous function
done();
});
// You can also use some of Sinon's functions to verify that the stub was in fact invoked
assert(this.authenticateStub.calledWith('user', 'pass'));
});
afterEach(function () {
this.authenticateStub.restore();
});
});
I hope this helps.
Related
In my code, I have:
function handleMessage() {
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
let recordings = twilio.recordings(foundConference.RecordingSid);
console.log('recordings', recordings);
return recordings.remove();
}
And in my stub, I have:
const sinon = require('sinon');
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
exports.twilioRecordings = () => {
console.log('about to stub', twilio.recordings);
sinon.stub(twilio, 'recordings').returns('here');
console.log('finished stub', twilio.recordings);
return;
};
However, it doesn't actually create a stubbed function. It's still using the original recordings function. What am I doing wrong?
Twilio npm package returns a function which creates a new object on every call, it's not a singleton. So your stubbed twilio instance is scoped to the test only.
Also twilio.recordings (as all other properties though) is defined through the getter function in prototype, so they are read only:
Object.defineProperty(Twilio.prototype,
'recordings', {
get: function() {
return this.api.account.recordings;
}
});
So, stubbing actual twilio instance have no effect. Except if you change the instance's prototype, but I don't think it worth doing for just unit testing.
I'd suggest you to refactor your code to put twilio initialization into separate method:
function getTwilio() {
return require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
}
Next your hangleMessage will look like:
function handleMessage() {
const twilio = this.getTwilio();
const recordings = twilio.recordings(...);
...
}
And next, in your test you can stub getTwilio() to return stub:
const twilioStub = {
recordings: sinon.stub(),
remove: sinon.stub()
}
sinon.stub(myAwesomeModule, 'getTwilio').returns(twilioStub);
You also can consider using mock-require package:
const sinon = require('sinon');
const mock = require('mock-require');
mock('twilio', () => ({
recordings: sinon.stub(),
}));
Here is a question about how-to mock dependencies, there might be other helpful libraries to stub required module.
Hope it helps.
Can Jest's mocking handle functions from modules I didn't write?
node-yelp-api-v3 has Yelp.searchBusiness(String) but my attempts to use Jest's mocking functionality are unsuccessful. The Jest examples seem to assume that I'm mocking a module I have in the project. From the documentation I'm also unclear how to mock a specific function in a module.
Neither of these are working:
jest.mock('Yelp.searchBusiness', () => {
return jest.fn(() => [{<stubbed_json>}])
})
or
jest.mock('Yelp', () => {
return jest.fn(() => [{<stubbed_json>}])
})
I'm currently using sinon but would like to use just Jest. This Sinon approach works:
var chai = require('chai')
var should = chai.should()
var agent = require('supertest').agent(require('../../app'))
const Yelp = require('node-yelp-api-v3')
var sinon = require('sinon')
var sandbox
describe('router', function(){
beforeEach(function(){
sandbox = sinon.sandbox.create()
stub = sandbox.stub(Yelp.prototype, 'searchBusiness')
})
afterEach(function(){
sandbox.restore()
})
it ('should render index at /', (done) => {
/* this get invokes Yelp.searchBusiness */
agent
.get('/')
.end(function(err, res) {
res.status.should.equal(200)
res.text.should.contain('open_gyro_outline_500.jpeg')
done()
})
})
})
Mocking external modules is explained here.
If the module you are mocking is a Node module (e.g.: lodash), the mock should be placed in the __mocks__ directory adjacent to node_modules (unless you configured roots to point to a folder other than the project root) and will be automatically mocked. There's no need to explicitly call jest.mock('module_name').
For your exact case this would mean you need to create a folder __mocks__ with a file node-yelp-api-v3.js in it. In that file you create a mock object from the original module using genMockFromModule and override the method you want to mock.
// __mocks__/node-yelp-api-v3.js
const yelp = jest.genMockFromModule('node-yelp-api-v3')
function searchBusiness() {
return [{<stubbed_json>}]
}
yelp.searchBusiness = searchBusiness
module.exports = yelp
Additionally you could also wrap the searchBusiness in jest.fn if you want to call assertions like searchBusiness.mock.calls.length for this method later.
You can also do this:
jest.mock('Yelp', () => ({
searchBusiness: jest.fn(() => [{<stubbed_json>}])
})
And then you'll be able to call things like expect(Yelp.searchBusiness).toHaveBeenCalled() etc.
I have a service module that is exported as a function. I need to pass a couple of things into it, like a configuration object so it does need to retain this structure. I am trying to stub out a function from the service but can't figure it out. In my app, I have a function that makes an API call that is problematic during testing so I'd like to stub it. (I understand I'd have to write my test differently to handle the async issue)
// myService.js
module.exports = function(config) {
function foo() {
returns 'bar';
}
return {
foo: foo
};
};
// test.js
var config = require('../../config');
var request = require('supertest');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var myService = require('./myService.js')(config);
describe('Simple test', function(done) {
it('should expect "something else", function(done) {
var stub = sinon.stub(myService, 'foo').returns('something else');
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
stub.restore();
})
.end(done);
});
});
* /testRoute I set up as a simple GET route that simply returns the value from myService.foo()
The above is not working, and I believe it has to do with the way my service is exporting. If I write the service as below, the stub works fine.
module.exports = {
test: function() {
return 'something';
}
};
But again, I need to be able to pass in information to the module so I would like to keep my modules in the original structure above. Is there a way to stub a function from a module that exports in that manner? I was also looking into proxyquire but not sure if that is the answer.
The reason why your test stub does not work is that the foo function is created every time the module initializer is called. As you discovered, when you have a static method on the module, then you are able to stub.
There are a variety of solutions to this problem-- but the simplest is to expose the method statically.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function foo() {
return 'bar'
}
It's ugly, but works.
What if the foo function has a closure to variables within the service (which is why it lives within the service initializer). Then unfortunately these need to be explicitly passed in.
// myService.js
module.exports = function(config) {
return {
foo: foo
};
};
var foo = module.exports.foo = function(config) {
return function foo() {
return config.bar;
}
}
Now you can safely stub the module.
However, how you are stubbing should be considered unsafe. Only if your test works perfectly does the stub get cleaned up. You should always stub within the before and after (or beforeEach and afterEach) fixtures, such as:
// We are not configuring the module, so the call with config is not needed
var myService = require('./myService.js');
describe('Simple test', function(done) {
beforeEach(function () {
// First example, above
this.myStub = sinon.stub(myService, foo).returns('something else');
// Second example, above
this.myStub = sinon.stub(myService, foo).returns(function () {
returns 'something else';
});
});
afterEach(function () {
this.myStub.restore();
});
it('should expect "something else", function(done) {
request(server) // this object is passed into my test. I'm using Express
.get('/testRoute')
.expect(200)
.expect(function(res) {
expect(res.body).to.equal('something else');
})
.end(done);
});
});
There are other options to be able to stub dependencies using dependency injection. I recommend you look at https://github.com/vkarpov15/wagner-core or my own https://github.com/CaptEmulation/service-builder
i'm trying to mock a node.js application, but it doesn't work as expected.
I have a node.js Module called GpioPlugin with following method:
function listenEvents(eventId, opts) {
if(!opts.pin) {
throw new Error("option 'pin' is missing");
}
var listenPort = new onOff(opts.pin, 'in', 'both', {persistentWatch: true});
listenPort.watch(function(err, value) {
process.emit(eventId+'', value);
});
}
if(typeof exports !== 'undefined') {
exports.listenEvents = listenEvents;
}
and now i want to write a test using sinon for this method, but i don't know how... What would be the best way to test this?
This tree parts would be fine, if they get tested:
Error (no problem)
generation of onOff (how?)
event with correct params
If it's not already, you're going to want to put onOff into a module so that your test can inject a stub in.
var sinon = require("sinon");
var process = require("process");
var onOffModule = require(PATH_TO_ONOFF); //See note
var gpio = require(PATH_TO_GPIO);
var onOffStub;
var fakeListenPort;
beforeEach(function () {
//Stub the process.emit method
sinon.stub(process, "emit");
//Constructor for a mock object to be returned by calls to our stubbed onOff function
fakeListenPort = {
this.watch = function(callback) {
this.callback = callback; //Expose the callback passed into the watch function
};
};
//Create stub for our onOff;
onOffStub = sinon.stub(onOffModule, "onOff", function () {
return fakeListenPort;
});
});
//Omitted restoring the stubs after each test
describe('the GpioPlugin module', function () {
it('example test', function () {
gpio.listenEvents("evtId", OPTS);
assert(onOffStub.callCount === 1); //Make sure the stub was called
//You can check that it was called with proper arguments here
fakeListenPort.callback(null, "Value"); //Trigger the callback passed to listenPort.watch
assert(process.emit.calledWith("evtId", "Value")); //Check that process.emit was called with the right values
});
});
Note: The exact mechanics of replacing onOff with a stub may vary depending how you require it.
Things get a little more complicated if you require onOff directly, rather than requiring a module that includes onOff. In that case I think you might need to look into something like proxyquire.
I'm testing a module, and I want to mock out a a dependency within that module. Let me frame my scenario, if I may:
In my module
myModule.prototype.func = function(callback) {
complexObj.doStuff('foo', function(err) {
callback(err, 'stuff');
});
};
So, I'm trying to basically mock complexObj. It doesn't really matter if I mock the entire object or just the doStuff function in this case. Let's assume that doStuff does something like interact with a web service or the filesystem. complexObj is being injected into myModule by dependency injection. I've been using Jasmine and Sinon to try to mock or stub this object and function, but I've had no luck, so I've resorted to something like this, which seems a little kludgy:
In my spec:
describe('Testing myModule', function() {
it('should do stuff', function() {
ComplexObj.prototype.doStuff = function(arg, callback) {
callback(null); // If no errors, 'doStuff' returns null indicating no errors
};
var complexObj = new ComplexObj();
new myModule(complexObj).func(function(err, results) {
// Set up expectations...
});
});
});
So, as you can see, I'm psuedo-mocking out the doStuff function in the ComplexObj object. Since I'm not concerned about ComplexObj or doStuff function, I'm just invoking the callback with 'null' indicating to func that there were no errors in doStuff. As I mentioned before, I feel there should be a better way to handle this? Suggestions?
With Jasmine, you would do something like this:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff');
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalledWith(args, callback);
});
Edit: Or you could set up expectations in your mocked doStuff:
var complexObj = {doStuff: null};
spyOn(complexObj, 'doStuff').andCallFake(function(args, callback) {
expect(args).toEqual(/*...*/);
expect(callback).toEqual(/*...*/);
callback();
});
new myModule(complexObj).func(function(err, results) {
expect(complexObj.doStuff).toHaveBeenCalled();
});