Unit test mongoose promises with sinon - node.js

I am trying to unit test a mongoose object that uses promises. I have written the test below and it works but it's not complete. I can't figure out how to test if the 'then' or 'catch' methods are called.
How can I use a spy to check that the 'then' method is called when I resolve the promise?
Method to test
export function create(req, res) {
User
.createAsync(req.body)
.then(handleCreate(res, req.originalUrl))
.catch(handleError(res));
}
Unit test
it('should do something', () => {
const req = {
body: 45,
};
const res = {};
const mockRole = sandbox.mock(Role).expects('createAsync').once().withArgs(45)
.returns(Promise.resolve());
controller.create(req, res);
});
UPDATE WITH SOLUTION I USED (May 6th, 2016)
Thanks #ReedD for helping me in the right direction
Although this "works", I feel like I'm testing the functionality of promises more than my code.
it('should call create with args and resolve the promise', () => {
const createSpy = sinon.spy();
const errorSpy = sinon.spy();
sandbox.stub(responses, 'responseForCreate').returns(createSpy);
sandbox.stub(responses, 'handleError').returns(errorSpy);
sandbox.mock(Role).expects('createAsync').once().withArgs(45)
.returns(Promise.resolve());
return controller.create(req, res).then(() => {
expect(createSpy.calledOnce).to.be.equal(true);
expect(errorSpy.calledOnce).to.be.equal(false);
});
});

You could add handleCreate and handleError to module.exports and then make stubs or spys of those. Below is an example of what I think you're trying to do. I also assumed you were using sinon/chai.
http://ricostacruz.com/cheatsheets/sinon-chai.html
// controller.js
module.exports = {
handleCreate: function () {
// ..code
},
handleError: function () {
// ..code
},
create: function (req, res) {
User
.createAsync(req.body)
.then(this.handleCreate(res, req.originalUrl))
.catch(this.handleError(res));
}
};
// test/test.js
var controller = require('../controller');
it('should do something', function (done) {
var handleCreate = sandbox.spy(controller, 'handleCreate');
var handleError = sandbox.spy(controller, 'handleError');
var mockRole = sandbox
.mock(Role)
.expects('createAsync')
.once().withArgs(45)
.returns(Promise.resolve());
var req = {
body: 45,
};
var res = {
send: function () {
expect(handleCreate).to.be.calledOnce;
expect(handleError).to.not.be.called;
done();
}
};
controller.create(req, res);
});

Related

sinon stub for lambda function which is inside another lambda

Am writing unit test case for my code, as am calling another lambda function inside my lambda am not sure how to mock the inner lambda value, so because of this my test case is getting timed out. Attaching my code below
Test case file
"use strict";
const sinon = require("sinon");
const AWS = require("aws-sdk");
const expect = require("chai").expect;
const models = require("common-lib").models;
const { Organization } = models;
const DATA_CONSTANTS = require("./data/deleteOrganization");
const wrapper = require("../../admin/deleteOrganization");
const sandbox = sinon.createSandbox();
describe("Start Test updateOrganization", () => {
beforeEach(() => {
sandbox.stub(Organization, "update").resolves([1]);
});
afterEach(async () => {
sandbox.restore();
});
it("Test 03: Test to check success returned by handler", async () => {
const mLambda = {
invoke: sinon.stub().returnsThis(),
promise: sinon.stub(),
};
const response = await wrapper.handler(
DATA_CONSTANTS.API_REQUEST_OBJECT_FOR_200
);
console.log({ response });
expect(response.statusCode).to.be.equal(200);
const body = JSON.parse(response.body);
expect(body.message).to.be.equal("Updated successfully");
});
});
Code function
exports.handler = asyncHandler(async (event) => {
InitLambda("userService-deleteOrganization", event);
const { id } = event.pathParameters;
if (isEmpty(id)) {
return badRequest({
message: userMessages[1021],
});
}
try {
const orgrepo = getRepo(Organization);
const [rowsUpdated] = await orgrepo.update(
{ isDeleted: true },
{ org_id: id }
);
if (!rowsUpdated) {
return notFound({
message: userMessages[1022],
});
}
const lambda = new AWS.Lambda({
region: process.env.region,
});
await lambda
.invoke({
FunctionName:
"user-service-" + process.env.stage + "-deleteOrganizationDetail",
InvocationType: "Event",
Payload: JSON.stringify({
pathParameters: { id },
headers: event.headers,
}),
})
.promise();
return success({
message: userMessages[1023],
});
} catch (err) {
log.error(err);
return failure({
error: err,
message: err.message,
});
}
});
It seems that you are not properly stubbing the AWS.Lambda object.
try this,
const sinon = require("sinon");
const AWS = require("aws-sdk");
const expect = require("chai").expect;
const models = require("common-lib").models;
const { Organization } = models;
const DATA_CONSTANTS = require("./data/deleteOrganization");
const wrapper = require("../../admin/deleteOrganization");
const sandbox = sinon.createSandbox();
describe("Start Test updateOrganization", () => {
beforeEach(() => {
sandbox.stub(Organization, "update").resolves([1]);
});
afterEach(async () => {
sandbox.restore();
});
it("Test 03: Test to check success returned by handler", async () => {
const mLambda = { invoke: sinon.stub().returnsThis(), promise: sinon.stub() };
// you missed the below line
sinon.stub(AWS, 'Lambda').callsFake(() => mLambda);
const response = await wrapper.handler(
DATA_CONSTANTS.API_REQUEST_OBJECT_FOR_200
);
console.log({ response });
expect(response.statusCode).to.be.equal(200);
const body = JSON.parse(response.body);
expect(body.message).to.be.equal("Updated successfully");
sinon.assert.calledOnce(AWS.Lambda);
sinon.assert.calledWith(mLambda.invoke, {});
sinon.assert.calledOnce(mLambda.promise);
});
});
I can see that,
You are writing entire logic inside your handler function. This makes it less testable.
To overcome this you can write your code in such a way that is divided into small functions, which are easy to mock in test case files or testable independently. Handler function should only make call to those functions and return the result to the caller.
for Eg.
Lambda Handler:
exports.lambdaHandler = async (event) => {
// do some init work here
const lambdaInvokeResponse = await exports.invokeLambda(params);
}
exports.invokeLambda = async (params) {
const response = await lambda.invoke(params).promise();
return response;
}
test cases:
it('My Test Case - Success', async () => {
const result = await app.lambdaHandler(event);
const invikeLambdaResponse = {
// some dummy response
};
sinon.replace(app, 'invokeLambda', sinon.fake.returns(invikeLambdaResponse ));
});
This is now mocking the only lambda invoke part.
You can mock all the external calls like this (dynamodb, invoke, sns, etc.)
You can set spy and check if the called method is called as per desired arguments

Mocha: Can't stub function in file that was already required in another test file

I'm using Mocha to run a test suite on fileToTest.js with requires greeting.js
// --fileToTest.js--
const { greeting } = require('./greeting');
module.exports = () => greeting();
// --greeting.js--
module.exports.greeting = () => 'hi!';
By itself, this test file successfully stubs greeting.
// --test2.js--
let parent;
const sinon = require('sinon');
const chai = require('chai');
const greeting = require('../../greeting.js');
const { expect } = chai;
describe('stubbed /hi', () => {
before(async () => {
sinon.stub(greeting, 'greeting').callsFake((req, res, next) => 'bye!');
parent = require('../../parent.js');
});
after(async () => {
greeting.greeting.restore();
});
it('should say bye', async function () {
expect(parent()).to.be.equal('bye!');
});
});
However if I run a test suite and have another test file that requires fileToTest.js, like test1.js below, the first test above (test2.js) won't stub greeting.
// --test1.js--
const chai = require('chai');
const fileToTest = require('../../fileToTest.js');
const { expect } = chai;
describe('not stubbed /hi', () => {
it('should say hi', () => {
expect(fileToTest()).to.be.equal('hi!');
});
});
It seems once test1.js requires fileToTest, mocha doesn't reload the fileToTest on the test2.js's require. So fileToTest is stuck with the old greeting function.
Whats the proper way to stub a function in this situation?
Repo
This answer worked. I had to delete the cache.
before(async () => {
delete require.cache[require.resolve('../../fileToTest.js')]; // <------
sinon.stub(greeting, 'greeting').callsFake((req, res, next) => 'bye!');
fileToTest = require('../../fileToTest.js');
});

mock third party method +jest

I am using jest for my backend unit testing.I need to mock third party library module methods using that.I tried the following code:
My controller file:
const edgejs = require('apigee-edge-js');
const apigeeEdge = edgejs.edge;
async get(req, res) {
const abc= await apigeeEdge.connect(connectOptions);
const Details = await abc.developers.get(options);
return res.status(200).send(Details);
}
test.spec.js
let edgejs = require('apigee-edge-js');
const ctrl = require('../../controller');
describe("Test suite for abc", () => {
test("should return ...", async() =>{
edgejs.edge = jest.fn().mockImplementationOnce(async () =>
{return {"connect":{"developers":{"get":[{}]}}}}
);
ctrl.get(req, res)
});
But its not mocking , its calling the actual library connect method. What i am doing wrong here. Please share your ideas. Thanks in advance.
WORKING CODE
jest.mock('apigee-edge-js', () => {
return { edge: { connect: jest.fn() } };
});
const edgejs = require('apigee-edge-js');
test("should return ...", async () => {
edgejs.edge.connect.mockImplementationOnce(() => Promise.resolve(
{"developers":{"get":[{}]}}
));
edgejs.edge.connect()
expect(edgejs.edge.connect).toBeCalled();
})
ERROR CODE:
jest.mock('apigee-edge-js', () => {
return { edge: { connect: jest.fn() } };
});
const Ctrl = require('../../controllers/controller'); ----> Extra line
const edgejs = require('apigee-edge-js');
test("should return ...", async () => {
edgejs.edge.connect.mockImplementationOnce(() => Promise.resolve(
{"developers":{"get":[{}]}}
));
const req = mockRequest();
const res = mockResponse();
await Ctrl.get(req, res) ---> Extra line
expect(edgejs.edge.connect).toBeCalled();
});
Receceivig erro : TypeError: edgejs.edge.connect.mockImplementationOnce is not a function
The mock doesn't affect anything because controller dereferences edgejs.edge right after it's imported, apigeeEdge = edgejs.edge. This would be different if it were using edgejs.edge.connect instead of apigeeEdge.connect.
Methods shouldn't be mocked as ... = jest.fn() because this prevents them from being restored and may affect other tests after that; this is what jest.spyOn is for. Furthermore, edge is an object and not a method.
Jest provides module mocking functionality. Third-party libraries generally need to be mocked in unit tests.
It should be:
jest.mock('apigee-edge-js', () => {
return { edge: { connect: jest.fn() } };
});
const ctrl = require('../../controller');
const edgejs = require('apigee-edge-js');
test("should return ...", async () => {
edgejs.edge.connect.mockImplementationOnce(() => Promise.resolve(
{"developers":{"get":[{}]}}
));
await ctrl.get(req, res)
...
});

nodejs unit test undefined after required controller

I am try to make a unit test for a controller in a project that use nodeJS, but when I call a function of a controller the test return that this controller is undefined.
This is my MK.js code:
MK = {}
MK.deleteAgent() {
}
module.exports = MK
My test code MK.spec.js:
const assert = require("assert");
const sinon = require("sinon");
const { MK } = require("./MK");
describe("MK controllers", function () {
let rt_model;
beforeEach(function () {
rt_model = {
findAll: sinon.fake.resolves()
}
});
describe("deleteAgent", function () {
it("should call rt_model.findAll", function (done) {
// a mock for the Web response
const response = {
status: () => null,
json: () => {
assert.strictEqual(rt_model.findAll.callCount, 1);
done();
}
};
MK.deleteAgent(null, response);
});
});
afterEach(() => {
// Restore the default sandbox here
sinon.restore();
});
});
But ther result that I get when run test is that MK is undefined

How to implement functional unit testing sinon with mocks in NodeJs?

How to implement sinon.mock on follwing function.
function getDashboard(req,res){
res.send("success");
}
describe("GetDashboard test"){
it("Response Should be test", function(){
const getDashboard = sinon.stub().returns('success');
let req = {}
let res = {
send: function(){};
const mock = sinon.mock(res);
mock.expect(getDashboard.calledOnce).to.be.true;
mock.verify();
}
})
}
Also how to stubbing data in function.Is it correct way of mocking.
Here is a working example:
const sinon = require('sinon');
function getDashboard(req, res) { res.send('success'); }
describe("getDashboard", function () {
it("should respond with 'success'", function () {
const req = {};
const res = { send: sinon.stub() };
getDashboard(req, res);
sinon.assert.calledWithExactly(res.send, 'success'); // Success!
})
});
Details
getDashboard calls the send function of the res object that it is given, so you just need to create a mock object with a sinon stub for the send property and verify that it was called as expected.

Resources