Mocha spies returns callCount 0 - node.js

I am using mocha, chai and sinon for unit testing in node env. I need to test a scenario where it makes a call to services to fetch the data and return the data.
My controller looks like this:
{
get model() { return schema},
async findUser(data) {
const data = await this.model.find({ id: data.id });
return data;
}
}
In my mocha test I am using the Sinon stub to return the model and find function some like this:
sinon.stub(controller, 'model').get(() => ({
find: () => ({ username: 'asdf' })
}));
My test is working as expected. Now I want to test see if my find method id called once and the arguments passed to it. To do that, i added following code
const spyFind = sinon.spy(controller.model, 'find');
assert.isTrue(spyFind.calledOnce);
This should return true because the spyFind is called and it returned the expected mock value. But when i debug, the spyFind object says isCalled 'false'. Can someone help me understand what am i doing wrong?

The way your stub is designed it is going to return a new object every time controller.model is called. As result:
controller.model === controller.model // false
So when you try to spy on the find property of controller.model with:
const spyFind = sinon.spy(controller.model, 'find');
Sinon grabs the object returned by controller.model and stubs find on that object. The the next time you call controller.model, for example in your test, you will get a fresh object from controller.model that is not spied on. So the original spy is never called.
I think better approach is to provide a singe stub that is returned by find(), then you can tell if the stub is called:
const sinon = require('sinon')
let controller = {
get model() { return schema},
async findUser(data) {
const data = await this.model.find({ id: data.id });
return data;
}
}
let findStub = sinon.stub()
findStub.returns({ username: 'asdf' })
sinon.stub(controller, 'model').get(() => ({
find: findStub
}));
console.log(controller.model.find()) // { username: 'asdf' }
console.log(findStub.calledOnce); // true

Related

How to mock the return value of messages.create() method from twilio-node using sinon js/loopback testlab?

I'm trying to mock the return value of messages.create() method from twilio-node library.
Since the create method resides inside the interface called messages, i can't directly mock the return value of create method.
My Unit test:
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from '#loopback/testlab';
import sinon from 'sinon';
import {Twilio} from '../../../../clients/whatsapp-sms-clients/twilio.whatsapp-sms-clients';
import twilio from 'twilio';
describe('Twilio client (UnitTest)', () => {
let twilioMock: StubbedInstanceWithSinonAccessor<twilio.Twilio>;
let logger: StubbedInstanceWithSinonAccessor<LoggingService>;
let twilioClient: Twilio;
beforeEach(() => {
twilioMock = createStubInstance(twilio.Twilio);
logger = createStubInstance(LoggingService);
twilioClient = new Twilio(twilioMock, logger);
});
it('should create the message', async () => {
twilioMock.stubs.messages.create.resolves({
// mocked value
});
});
});
Thanks in advance.
Twilio developer evangelist here.
I've not worked with testlab/sinon like this before, but I think I have an idea of what you need to do, if not the right syntax.
You'd need to stub the response to twilioMock.messages to return an object that has a create property that is a stubbed function that resolves to the result you want. Something like this might work, or at least set you on the right track:
it('should create the message', async () => {
// Create stub for response to create method:
const createStub = sinon.stub().resolves({
// mocked value
});
// Stub the value "messages" to return an object that has a create property with the above stub:
twilioMock.stubs.messages.value({
create: createStub
});
// Rest of the test script
});
Edit
OK, using value above didn't work. I tried again. This version strips out your custom Twilio wrapper from the example and just calls things directly on the Twilio client stub itself. Hopefully you can use this as inspiration to work it into your tests.
What I realised is that twilioClient.messages is a getter and is dynamically defined. So, I directly stubbed the result on the stub client.
import {
createStubInstance,
StubbedInstanceWithSinonAccessor,
} from "#loopback/testlab";
import sinon from "sinon";
import { Twilio } from "twilio";
describe("Twilio client (UnitTest)", () => {
let twilioMock: StubbedInstanceWithSinonAccessor<Twilio>;
beforeEach(() => {
twilioMock = createStubInstance(Twilio);
});
it("should create the message", async () => {
const createStub = sinon.stub().resolves({
sid: "SM1234567",
});
sinon.stub(twilioMock, "messages").get(() => ({
create: createStub,
}));
const message = await twilioMock.messages.create({
to: "blah",
from: "blah",
body: "hello",
});
expect(message.sid).toEqual("SM1234567");
});
});
The above test passes for me in my setup.

Mocking with Sinon against a service module

I have got myself to the stage of unit testing, and to be honest, with all the different examples online I have got myself confused. I have a good understanding of Mocha & Chai, but Sinon is a different story.
So I have what I think is a pretty straight forward setup. I have a POST route that calls a controller. This controller is like so (removed some basic validation code)
const { createUser } = require('../services/user.service');
const apiResponse = require('../helpers/apiResponse');
const postUser = async (req, res) => {
const user = {
account_id: req.body.id,
status: req.body.status,
created_at: new Date(),
updated_at: new Date(),
};
const result = await createUser(user);
return apiResponse.successResponseWithData(res, 'User added.', result.affectedRows);
} catch (err) {
return apiResponse.errorResponse(res, err);
}
};
module.exports = {
postUser,
};
So all it really does is validate, and then creates a user object with the req and pass that to a service class. This services class does nothing more than pass the data to a database class.
const { addUserToDb } = require('../database/user.db');
const createUser = async (user) => {
try {
const createdUser = await addUserToDb(user);
return createdUser;
} catch (err) {
throw new Error(err);
}
};
module.exports = {
createUser,
};
I wont show the database class because what I want to focus on first is the controller, and then I can hopefully do the rest myself.
So from what I understand, I should be testing functions. If a function makes an external call, I should spy, mock, stub that call? I should only spy, mock or stub this functions dependencies, if one of the dependencies
has its own dependency (like the service module above having a database call dependency), this should be performed in another test? Sorry, just a few questions to help me understand.
Anyways, so I have created a user.controller.test.js file. I have not got far with it, but this is what I have so far
const chai = require('chai');
const sinon = require('sinon');
const { expect } = chai;
const faker = require('faker');
const controller = require('../controllers/user.controller');
const service = require('../services/user.service');
const flushPromises = () => new Promise(setImmediate);
describe('user.controller', () => {
describe('postUser', () => {
beforeEach(() => {
//I see a lot of code use a beforeEach, what should I be doing here?
});
it('should create a user when account_id and status params are provided', async () => {
const req = {
body: { account_id: faker.datatype.uuid(), status: 'true' },
};
const stubValue = {
id: faker.datatype.id(),
account_id: faker.datatype.uuid(),
status: 'true',
created_at: faker.date.past(),
updated_at: faker.date.past(),
};
});
});
});
If I am being totally honest I am pretty lost as to what I should be testing here. From my understanding, I need to mock the service module I think.
Could someone kindly provide some insight as to what I should be doing in this test?
Many thanks
Update
Thank you for your detailed response, I have managed to get a spy working which is a step forward. So I want to do a test on my service module, createUser method.
You can see that my createUser method takes a user Object as a parameter and passes this to a database module where it is inserted into the database and then the user object returned.
So when testing my service class, I need to mock this call to my database module.
const chai = require('chai');
const sinon = require('sinon');
const { expect } = chai;
const faker = require('faker');
const service = require('../services/user.service');
const database = require('../database/user.db');
describe('user.service', () => {
describe('createUser', () => {
it('should create a user when user object is provided', async () => {
const user = {
id: faker.datatype.string(),
status: 'true',
created_at: faker.date.past(),
updated_at: faker.date.past(),
};
const expectedUser = {
id: user.id,
status: user.status,
created_at: user.created_at,
updated_at: user.updated_at,
};
const mockedDatabase = sinon.mock(database);
mockedDatabase.expects('addUserToDb').once().withArgs(expectedUser);
await service.createUser(user);
mockedDatabase.verify();
mockedDatabase.restore();
});
});
});
When I test this, I seem to be getting this response, and it still seems to be inserting the record into my database.
ExpectationError: Expected addUserToDb({
id: 'yX7AX\\J&gf',
status: 'true',
created_at: 2020-06-03T03:10:23.472Z,
updated_at: 2020-05-24T14:44:14.749Z
}, '[...]') once (never called)
at Object.fail (node_modules\sinon\lib\sinon\mock-expectation.js:314:25)
Do you have any idea what I am doing wrong?
Thanks
before I try, I would like to suggest to drop the try/catch blocks everywhere, I will assume you're using expressJs in your Node application, and for such, take a look at express-promise-router as using that Router (instead the default one) will automatically catch anything it was thrown and you just need to focus on the code...
taking your example, you would write:
const { addUserToDb } = require('../database/user.db');
const createUser = async (user) => addUserToDb(user);
module.exports = {
createUser,
};
and
const { createUser } = require('../services/user.service');
const apiResponse = require('../helpers/apiResponse');
const postUser = async (req, res) => {
const { id: account_id, status } = res.body;
const result = await createUser({ account_id, status }); // set the date in the fn
return apiResponse.successResponseWithData(res, 'User added.', result.affectedRows);
};
module.exports = {
postUser,
};
if there's an error and in some place on the route an error is thrown, you will get a nice message back in the response with the error
regarding the code it self, seems a lot cleaner to read - keep in mind that code is for humans, the machine does not even care how you name your variables 😊
Now, regarding the tests ... I do tend to split things into 3 parts
unit tests: the functions itself, single one, like validation, helpers, etc
integration tests: when you call your API endpoint what should be returned
GUI tests (or end-to-end/e2e): applied when a GUI exists, will skip this for now
so in your case, the first thing to make sure of is what are you testing... and taking that, start from the small blocks (unit tests) and move up to the blocks that make sure all is glued together (e2e)
So all it really does is validate, and then creates a user object with the req and pass that to a service class. This services class does nothing more than pass the data to a database class.
Seems a great way to start, so it "validates" ... let's test our validation, let's pass null, undefined, string when all you want is int and so on, until we get a pretty good idea that whatever it passes, we will reply correctly with and without an error
Note I tend to use OpenAPI specs, which makes things easier for me as it provides 2 things
documentation of the endpoints
validation of the endpoints with a nice error message to the consumer
and yes, I always test some validation just to make sure it's working as expected, even though I trust the tool 100% 😜
So from what I understand, I should be testing functions.
well, an application is a group of functions, so all good there 💪
If a function makes an external call, I should spy, mock, stub that call?
I'll try to explain as best as I can what spies, stubs and mocks in Sinon are, please be gentle 🙏
Spies
they tell us information about functions calls, like, number of times called, arguments, return value, and more - they have two types, anonymous spies or spies that wrap methods in our code
function testMyCallback(callback) { callback(); }
describe('testMyCallback fn', function() {
it('should call the callback', function() {
const callbackSpy = sinon.spy(); // anonymous spy - no arguments
testMyCallback(callbackSpy);
expect(callbackSpy).to.have.been.calledOnce;
});
});
const user = {
setNname: function(name) {
this.name = name;
}
}
describe('setname fn', function() {
it('should be called with name', function() {
const setNameSpy = sinon.spy(user, 'setName'); // wrap method spy
user.setName('Katie');
expect(setNameSpy).to.have.been.calledOnce;
expect(setNameSpy).to.have.been.valledWith('Katie');
setNameSpy.restore(); // to remove the Spy and prevent future errors
});
});
Stubs
are power-spies, as they have all the functionality of Spies, but they replace the target function, they have methods that can return a specific value or throw a specific exception and a bit more
they are great to be used with your question regarding external calls, as they replace calls (so you can mock the call behavior and never use the original call)
the simplest of the examples is:
function isAdult(age) {
return age > 21;
}
describe('Sinon Stub Example', () => {
it('should pass', (done) => {
const isAdult = sinon.stub().returns('something');
isAdult(0).should.eql('something');
isAdult(0).should.not.eql(false);
done();
});
});
we've STUB'ed our function, and explicitly said it's a "function" that returns a string something... and for now on, we will never need to go to the function itself, as we have STUB it, we've replaced the real behavior with our own
another example of using STUBs when calling our API application in our integration tests
describe('when we stub our API call', () => {
beforeEach(() => {
this.get = sinon.stub(request, 'get'); // stub "request.get" function
});
afterEach(() => {
request.get.restore(); // remove our power-spy
});
describe('GET /api/v1/accounts', () => {
const responseObject = {
status: 200,
headers: {
'content-type': 'application/json'
}
};
const responseBody = {
status: 'success',
data: [
{
accountId: 1,
status: 'active'
},
{
accountId: 2,
status: 'disabled'
}
]
};
it('should return all accounts', (done) => {
// the 3 objects of our callback (err, res, body)
this.get.yields(null, responseObject, JSON.stringify(responseBody));
request.get(`${base}/api/v1/movies`, (err, res, body) => {
expect(res.statusCode).to.be.eql(200);
expect(res.headers['content-type']).to.contain('application/json');
body = JSON.parse(body);
expect(body).to.be.an('array').that.includes(2);
done();
});
});
});
});
you can also stub axios, but you will need a new library, either moxios, or proxyquire or more...
Mocks
are a bit similar to Stubs (our Power-Spies) but they can be used to replace whole objects and alter their behavior, they are mostly used when you need to stub more than one function from a single object - if all you need is to replace a single function, a stub is easier to use
Mocks can make things oversimplify and you could break your application without even knowing, so be aware...
a normally use is, for example
function setupNewAccount(info, callback) {
const account = {
account_id: info.id,
status: info.status,
created_at: new Date(),
updated_at: new Date()
};
try { Database.save(account, callback); }
catch (err) { callback(err); }
}
describe('setupNewAccount', function() {
it('', function() {
const account = { account_id: 1, status: 'active' };
const expectedAccount = {
account_id: account.id, status: account.status
};
const database = sinon.mock(Database);
database.expectes('save').once().withArgs(expectedAccount);
setupNewAccount(account, function() {});
database.verify();
database.restore();
});
});
something that we will keep forgetting is the .restore() part, and for that, there's a package (one more...) called sinon-test that will auto cleanup at the end of a test
I just hope it helped you with some of your questions and it's a bit clearer now 😏
BTW, for stubbing HTTP requests, I use nock as I think it's much easier to read and use than Sinon, especially for anyone that is reading code for the first time and has no experience in either Sinon or Nock...

Mock an axios request to external service in test

I'm trying to mock a call to an external service from NodeJS express. I can't get axios-mock-adapter to intercept the actual axios call (http://api.openweathermap.org) and to return a mocked response. When the assert is done it fails because to values are different. The temperature from the call is the actual outside temperature and not the mocked one. Don't know if I'm totally off or if I'm to close to the solution that I cant see it. Kind of new to JavaScript and NodeJS.
Please Stackoverflow, you are my only help.
This is my code:
File to test:
WeatherTools.prototype.getWeather = new Promise(function(resolve, reject) {
axios.get(config.weather.openWeatherLocationURL, {
params: {
id: config.weather.openWeatherMapLocation,
APPID: config.weather.openWeatherMapApiKey,
units: config.weather.openWeatherUnit
}
}
).then(function(axiosResponse) {
resolve(axiosResponse.data);
}).catch(function(axiosError) {
reject(axiosError);
});
});
Test file:
const assert = require('assert');
const weatherTool = require('./weatertools');
const axios = require('axios');
let MockAdapter = require('axios-mock-adapter');
const TestData = require('./testData.js');
let mock = new MockAdapter(axios);
describe("Testing weather tools", () => {
beforeEach(function() {
mock
.onGet(config.weather.openWeatherLocationURL, {
params: {
id: config.weather.openWeatherMapLocation,
APPID: config.weather.openWeatherMapApiKey,
units: config.weather.openWeatherUnit
}
}).reply(200, TestData.location().mockedResponseData);
});
it('given a correct call to openWeather a valid response should be returned xxx', function(done) {
weatherTool.WeatherTools.getWeather.then(function(actual) {
assert.strictEqual(actual.temp.currentTemp, TestData.location().expected.temp.currentTemp);
done();
})
});
});
Config file:
config.weather.openWeatherMapApiKey = 'theSecretApiKeyOfMine';
config.weather.openWeatherMapLocation = '1234567';
config.weather.openWeatherUnit = 'metric';
config.weather.openWeatherLocationURL = 'http://api.openweathermap.org/data/2.5/weather';
The problem is in tested code. It's expected that getWeather member is a method that gets weather, while it's promise property. It doesn't get weather, it's actually weather. Since it's prototype property, it eagerly performs a request on class definition, i.e. as soon as the class is imported. This also means that data cannot be updated in future, even if there's a need to do this.
Since the request is eagerly performed, it won't be affected by Axios mock.
getWeather also uses promise construction antipattern; axios already returns a promise, there's no need to construct another one with new Promise.
It should be:
WeatherTools.prototype.getWeather = () => {
return axios.get(config.weather.openWeatherLocationURL, {
params: {
id: config.weather.openWeatherMapLocation,
APPID: config.weather.openWeatherMapApiKey,
units: config.weather.openWeatherUnit
}
}
).then(function(axiosResponse) {
return axiosResponse.data;
});
});
So it could be fetched like weatherToolsInstance.getWeather().then(...). It's expected that it will be mocked with axios-mock-adapter when used like that.

Sinon spy calledWithNew not working

I'm trying to use sinon or sinon-chai's calledWithNew (or simply called), but can't seem to get it to work, I've looked at a few suggestions online without luck either, here is the function I'm trying to test:
users.js
exports.create = function (data) {
//some validation
var user = new User(data);
return user.save().then((result) => {
return mailer.sendWelcomeEmail(data.email, data.name).then(() => {
return {
message: 'User created',
userId: result.id
};
});
}).catch((err) => {
return Promise.reject(err);
});
}
Here is my test:
users.test.js
beforeEach(() => {
saveStub = sandbox.stub(User.prototype, 'save').resolves(sampleUser);
spy = sandbox.spy(User);
});
afterEach(() => {
sandbox.restore();
});
it('should call user.save', async () => {
result = await users.create(sampleArgs);
expect(saveStub).to.have.been.called; //-> true
expect(spy).to.have.been.called; //-> false, calledWithNew returns same result as well
});
I found several posts suggesting spying on (window, 'className') but I'm using mocha, not a browser.
Trying to spy on (global, User / User.prototype) didn't work either.
User is a module-level variable in users.js. Sinon cannot affect it. When you do this in your test file:
spy = sandbox.spy(User);
You're creating a spy in the scope of your test file, sure, but your module is still using the original.
To do something like this, you need to export your constructor inside an object, then both invoke it and spy on it through that object:
Wherever User is coming from, let's say user.js:
class User {
// whatever your User implementation is
}
module.exports = { User };
users.js:
const userModule = require('./user');
...
var user = new userModule.User(data);
Then, in your test file:
const userModule = require('./user');
spy = sandbox.spy(userModule, 'User');
Another way to do this would be to use something like proxyquire. It can make these kinds of tests less obtrusive, but can make your tests more confusing to readers.
My preference is generally to keep constructors very simple so I don't ever have to spy on them. I've never used calledWithNew in any of my own projects for this reason. :\ It's up to you, though.

Sinon not stubbing on module.exports

If create an file with the following contents
const validateEmail = email => {
sendEmail(email);
};
const sendEmail = email => {
return true;
};
module.exports = {
validateEmail,
sendEmail,
};
And a test that tries to stub out the second function...
it('Should call sendEmail if a valid email is passed', () => {
let sendEmailSpy = sinon.stub(checkEmail, 'sendEmail');
checkEmail.validateEmail('acorrectemail#therightformat.com');
assert.isTrue(sendEmailSpy.called);
});
It still calls the sendEmail function and the test fails
However, if I write the module.exports like this:
module.exports = {
validateEmail(email) {
this.sendEmail(email);
},
sendEmail(email) {
return true;
},
};
It stubs it correctly...Why?
Short answer - context
Long answer - in the first scenario, the exported sendEmail function is not the same as the internal one that is used by validateEmail. The exported function becomes a new property of the object being exported and simply references the internal one.
In the second scenario, you explicitly reference the sendEmail function on the exported object (i.e. this.sendEmail(...)) from validateEmail therefore it will use the stubbed version.
Moral of the story - you can't stub something you can't see.

Resources