Sinon stubs not being used - node.js

I have a lambda function which is referencing a module define in another file.
Instead of having to get all the scenarios right in my tests I'd like to just have a stub of that module.
In app.js
var Service = require("./Services/Service");
let service = new Service(event.Environment);
var regulatorResults = await service.GetModifiers(budgeted, irrelevant, variables);
Service is defined as follows:
function Service(environment) {
async function GetModifiers(ids, irrelevant, things) {
//logic happens
}
return { GetModifiers };
}
module.exports = Service;
I've tried many different iterations in my tests but where I feel closest is with this:
var assert = require('assert');
var sinon = require('sinon');
var service = require('../Services/Service');
describe('Calculator', function () {
before(function () {
var service = sinon.createStubInstance(regulatorService);
sinon.stub(service.prototype, "GetModifiers").callsFake(async function(IDs, thing, thing) {
return IDs.map(id=> new { ID: id, Modifier: 1 });
});
});
When I run the actual tests, the results of that service are [], even if I code the stub to return a set value, and if I throw in a console.log, it doesn't get hit ever.
The tests fail with error Error: Trying to stub property 'GetModifiers' of undefined.
Thanks for your help!

Related

How to spy/stub/mock firestore db.collection().add() when db is a custom class property, i.e. how to stub this.db.collection().add()?

I have a class, Firestore, that inits a firebase db in the constructor, this.db, and has an addEntry() method that adds a new entry to the db. How can I stub/mock the write to the db so that no writes are made during testing? The assertion of this test is that db.collection().add() is called once.
firestore.js
class Firestore {
constructor() {
this.db = firestoreAdmin.firestore()
}
async addEntry(newEntry) {
newEntry.claimed = "false"
var collectionReference = await this.db.collection('collection_name').add(newEntry)
return collectionReference._path.segments[1]
}
}
test_firestore.js
const sinon = require('sinon')
const chai = require('chai')
const Firestore = require('../firestore.js')
describe('file firestore.js | class Firestore', function() {
const firestore = new Firestore()
describe('method addEntry(newEntry)', function() {
it('should call this.db.collection().add() once', function() {
var newEntry = {
"client": "clientName"
}
var add = sinon.spy(firestore.db.collection, 'add')
firestore.addEntry(newEntry)
sinon.assert.calledOnce(add)
add.restore()
})
})
})
Right now I'm getting this error:
1 failing
1) file firestore.js | class Firestore
method addEntry(newEntry)
should add key:value pair (claimed: false) prior to writing to db:
TypeError: Attempted to wrap undefined property add as function
Instead of spy sinon doc, consider use stub sinon doc. A spy will wrap the original function and does exactly what the original function does, in your case, write to the database.
Meanwhile, a stub should be used when you want to prevent a specific method from being called directly.
var add = sinon.stub(firestore.db.collection, 'add')
With the comments below, it looks like you are trying to stub a complex object, in this case, you can actually assign a new value to the property without any sinon methods like this:
const fakeAdd = sinon.fake()
firestore.db.collection = [
{add: fakeAdd}
]
firestore.addEntry();
sinon.assert.calledOnce(fakeAdd)
And for async method unit testing, you can simply mark the test method as async as well.
it('should do something', async () => {
await firestore.addEntry()
})
A working codepen example:
https://codepen.io/atwayne/pen/VweOXpQ

Why can't I properly stub the twilio library with sinon?

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.

How can I mock a function/property of a node module exported as a function using sinon?

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

Sinon.js and testing with events and new

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.

How do I use Gently to stub a method of an object that is not immediately exposed by a module?

I'm using the AWS SDK for NodeJS.
I've got a module (moduleFoo) set up like this:
if (global.GENTLY) { require = GENTLY.hijack(require); }
var aws = require("aws-sdk"),
ec2;
exports.initEC2Client = function () {
ec2 = new aws.EC2();
};
exports.doSomething = function () {
var params;
// params gets populated here...
ec2.Client.describeInstances(params, function (err, data) {
// logic!
}
}
I'm trying to stub out the describeInstances method.
I know I can stub the EC2 class by doing:
gently.stub("aws-sdk", "EC2");
and I can create a fake instance of that by stubbing its constructor, as per the Gently docs:
var ec2Stub = gently.stub("aws-sdk", "EC2"),
ec2;
gently.expect(ec2Stub, "new", function () {
ec2 = this;
});
moduleFoo.initEC2Client();
At this point I get stuck. What I need to do is to stub out a method of an object belonging to ec2. Is there a way to approach this using Gently?
I discovered the answer after having typed out the entire question, just before pressing submit.
I solved it by initialising Client as an empty object and then stubbing the describeInstances method on it:
var ec2Stub = gently.stub("aws-sdk", "EC2"),
ec2;
gently.expect(ec2Stub, "new", function () {
ec2 = this;
});
moduleFoo.initEC2Client();
ec2.Client = {};
gently.expect(ec2.Client, "describeInstances", function (params, callback) {
// assert `params` is populated ok
callback();
})
moduleFoo.doSomething();
gently.verify(); // throws no error

Resources