Sinon stub not working if tested using express app - node.js

I have a a controller function like below.
SendOTPController.js
const otpService = require('../services/otpService')
module.exports = async function(req, res) {
const {error, data} = await sendOTP(req.query.phone)
if(error)
return res.send(error)
return res.send(data)
}
otpService.js
module.exports = async function(phone) {
await result = fetch(`http://api.send-otp?phone=${phone}`)
if (result !== sucess)
return {
error: "Failed to send OTP!"
data: null
}
return {
error: null
data: result
}
}
Below is my test.
const expect = require('chai').expect
const request = require('supertest')
const sinon = require('sinon')
const rewire = require('rewire')
const SendOTPController= rewire('../../src/controllers/SendOTPController')
const app = require('../../src/app')
describe('GET /api/v1/auth/otp/generate', function () {
it('should generate OTP', async () => {
let stub = sinon.stub().returns({
error: null,
data: "OTP sent"
})
SendOTPController.__set__('sendOTPOnPhone', stub)
const result = await request(app)
.get('/api/v1/auth/otp/generate?phone=8576863491')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200)
console.log(result.body)
expect(stub.called).to.be.true
})
})
In above code the stub is not being called.
But if use only controller without using express app it works fine.
const expect = require('chai').expect
const request = require('supertest')
const sinon = require('sinon')
const rewire = require('rewire')
const SendOTPController= rewire('../../src/controllers/SendOTPController')
const app = require('../../src/app')
describe('GET /api/v1/auth/otp/generate', function () {
it('should generate OTP', async () => {
let stub = sinon.stub().returns({
error: null,
data: "OTP sent"
})
SendOTPController.__set__('sendOTPOnPhone', stub)
const result = await SendOTPController() // not using express app, hence not passing req, res
console.log(result)
expect(stub.called).to.be.true
})
})
I went through many modules and docs.
They give a solution how I can stub a module.exports = async function(){}.
They also work, but only If they are directly imported and tested.
They don't work if I use it with express app.
Any help would be appreciated, thanks.

Instead of returns try to use resolves:
let stub = sinon.stub().resolves({
error: null,
data: "OTP sent"
})
returns is for sync code, resolves for async.

Related

Mock api request Jest NodeJs

I'm trying to test the following code:
adapter.js
async function adapt(message) {
let parser = JSON.parse(message.content.toString());
let apiResult = await api(parser.id);
let result = apiResult.data.data;
return adapptedMessage = {"id": result.id}
}
This is my api call.
server.js
const axios = require('axios');
const url = process.env.URL;
function getApi(id) {
return axios.get(url + id).catch(function (error) {
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}});
}
module.exports = getApi
This is how I tried to test.
test.js
jest.mock('./server');
const axios = require('axios');
const adapt = require('./adapter');
describe("Adapter Test", () => {
test("adapt", async () => {
var result = await adapt(getMessage());
const mockResp = {"data":{"data": {"id":10}}};
axios.get = jest.fn(() => mockResp);
assert
expect(result).toStrictEqual(getOfferMessage());
});
})
function getMessage() {
return {"content":"{\"id\":10}"};
}
This is my first test in js, and I don't know how to mock the api call.
All I get is "undefined".
Could you help me?
Thanks
You should pass a factory function to jest.mock when mocking your server module
const mockResp = {"data":{"data": {"id":10}}};
jest.mock('./server', () => () => mockResp);
const adapt = require('./adapter');
describe("Adapter Test", () => {
test("adapt", async () => {
const result = await adapt(getMessage());
expect(result).toStrictEqual({ id: 10 });
});
})
function getMessage() {
return {"content":"{\"id\":10}"};
}

Mocking function to unit test Serverless Lambda

I am really struggling to understand unit testing within a Serverless Application. So I obviously have my handler, and I have a single Lambda function
const responses = require('../utils/jsonResponse');
const someConnector = require('../services/connectToService/connectToService');
module.exports = async (event) => {
const connectionParams = {
//some env variables
};
try {
const token = await someConnector.connectToService(connectionParams);
return responses.status(token, 200);
} catch (e) {
return responses.status(
`Issue connecting to service - ${e.message}`,
500,
);
}
};
So this Lambda function is pretty straight forward, gets some environment variables, and awaits a response from a service. It then returns the response.
So I have already done integration tests for this which is fine, but now I wanted to do a Unit test. I wanted to test this function in isolation, so essentially I want to mock connectToService to return my own responses.
So I came up with the following
require('dotenv').config();
const { expect } = require('chai');
const sinon = require('sinon');
let sandbox = require("sinon").createSandbox();
const LambdaTester = require('lambda-tester');
const handler = require('../../../handler');
const msConnector = require('../../../services/connectToService/connectToService');
describe('Testing handler', async (done) => {
describe('endpoint someEndpoint returns 200', () => {
it('Should resolve with 200', async () => {
before(() => {
sandbox = sinon.createSandbox();
sandbox.stub(msConnector, 'connectToService').resolves('some-token');
});
afterEach(() => {
sandbox.restore();
});
await LambdaTester(handler.someEndpoint)
.expectResult((result) => {
console.log(result);
expect(result.statusCode).to.equal(200);
});
});
});
done();
});
msConnector is the filename of the service, connectToService is the function name. What I want to do is not invoke this function, but return some-token when my Lambda calls it.
However, I have the console.log, and what I get from that is the real token, not some-token.
This tells me that the mocked function is really being called and executed and returning the real value.
So how can I mock this to make sure it returns some-token?
Thanks
Service function
const { DOMParser } = require('#xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');
const connectToService = async (connectionParams) => {
//this injects config details into XML
const xmlRequest = loginRequest(
connectionParams.username,
connectionParams.password,
connectionParams.url,
);
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': xmlRequest.length,
},
};
const token = await axios
.post(connectionParams.msHost, xmlRequest, config)
.then((res) => {
const dom = new DOMParser().parseFromString(res.data, 'text/xml');
if (
dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0)
) {
return dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0).firstChild.nodeValue;
}
throw new Error('Invalid Username/Password');
})
.catch((err) => {
throw new Error(`Error making connection - ${err.message}`);
});
return token;
};
module.exports = {
connectToService,
};
The function connectToService may be not same copy between you mocked and called.
Because you overwrote a new object by module.exports = .... This causes you probably get different object for each require.
Try to do the below approach sharing the same object for all require.
const { DOMParser } = require('#xmldom/xmldom');
const axios = require('axios');
const { loginRequest } = require('./xml/login');
const connectToService = async (connectionParams) => {
//this injects config details into XML
const xmlRequest = loginRequest(
connectionParams.username,
connectionParams.password,
connectionParams.url,
);
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': xmlRequest.length,
},
};
const token = await axios
.post(connectionParams.msHost, xmlRequest, config)
.then((res) => {
const dom = new DOMParser().parseFromString(res.data, 'text/xml');
if (
dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0)
) {
return dom.documentElement
.getElementsByTagName('wsse:secToken')
.item(0).firstChild.nodeValue;
}
throw new Error('Invalid Username/Password');
})
.catch((err) => {
throw new Error(`Error making connection - ${err.message}`);
});
return token;
};
module.exports.connectToService = connectToService;

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

How to test class called or not using mocha & chai with sinon TDD?

I want to test class called or not in nodejs, mocha & chai with sinon. I tried with stub but not worked as what I expected.
someMiddleware.js
module.export.someMiddleware = async(req,res,next)=>{
const responseData = await someReturnFunction(req);
if (!responseData || responseData == null) {
throw new SomeExtendedErrrorClass("stringArg");
}
res.send(responseData);
}
testFile.js
sinon
.stub(someMiddleWare , "someReturnFunction")
.returns(null);
const stubClass = sinon.stub(SomeExtendedErrrorClass, "constructor");
someMiddleware(req, res, next);
expect(stubClass).to.have.be.called;
Even the SomeExtendedErrrorClass called, sinon not detected.
Sinon does not support stub standalone function or class imported from other modules. You need to use Link Seams, we will be using proxyquire to construct our seams.
E.g.
someMiddleware.js:
const someReturnFunction = require('./someReturnFunction');
const SomeExtendedErrrorClass = require('./SomeExtendedErrrorClass');
module.exports.someMiddleware = async (req, res, next) => {
const responseData = await someReturnFunction(req);
if (!responseData || responseData == null) {
throw new SomeExtendedErrrorClass('stringArg');
}
res.send(responseData);
};
SomeExtendedErrrorClass.js:
class SomeExtendedErrrorClass extends Error {}
someReturnFunction.js:
async function someReturnFunction() {
return 'real implementation';
}
someMiddleware.test.js:
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const SomeExtendedErrrorClass = require('./SomeExtendedErrrorClass');
chai.use(chaiAsPromised);
const { expect } = chai;
describe('68640048', () => {
it('should get and send response data', async () => {
const someReturnFunctionStub = sinon.stub().resolves('teresa teng');
const { someMiddleware } = proxyquire('./someMiddleware', {
'./someReturnFunction': someReturnFunctionStub,
});
const mReq = {};
const mRes = { send: sinon.stub() };
await someMiddleware(mReq, mRes);
sinon.assert.calledWithExactly(mRes.send, 'teresa teng');
});
it('should throw error', async () => {
const someReturnFunctionStub = sinon.stub().resolves(null);
const { someMiddleware } = proxyquire('./someMiddleware', {
'./someReturnFunction': someReturnFunctionStub,
});
const mReq = {};
const mRes = { send: sinon.stub() };
await expect(someMiddleware(mReq, mRes)).to.eventually.rejectedWith(SomeExtendedErrrorClass);
});
});
test result:
68640048
✓ should get and send response data
✓ should throw error
2 passing (10ms)

Unit test with promise handling - node.js

i want to do a unit test with async function on the code. and here is my code on user.test.js
'use strict'
const UserDomain = require("../../../../../../bin/modules/users/repositories/commands/domain")
const UserHandler = require("../../../../../../bin/modules/users/repositories/commands/command_handler")
const expect = require('chai').expect;
const assert = require('chai').assert;
const sinon = require('sinon');
describe('User domain', () => {
describe('".login(data)"', () => {
let user;
beforeEach( () => {
user = {
clientId : "adithyavisnu",
clientSecret : "secretOfmine#19"
}
});
it("should return error when username/password is empty", (done)=> {
done();
// let
})
it("should return object", async () => {
const domainStub = sinon.stub(UserDomain.prototype, 'login');
const result = await UserHandler.login(user);
sinon.assert.calledOnce(domainStub);
domainStub.restore();
})
});
});
If the normal code (not the unit test code above) the const result = await UserHandler.login(user); will have an object response, but when i do in user.test.js it do not get the response. the result is undefined.
here are the user_handler code
'use strict';
const User = require('./domain');
const login = async (data) => {
const postData = async () => {
const user = new User();
const result = await user.login(data);
return result;
}
const response = await postData();
return response;
}
Is there something i did wrong on the code or some code is missing?
I am sorry if you do think there is unclear information
Thank you for the responses
In the normal flow, the UserHandler calls the Domain.login method and returns the result object. When you run the unit test you are stubbing the Domain.login method. so, it wont return the result as normal flow. You can either make the stub return some result object and test that or just spy the Domain.login instead of stubbing it , if you just want to just check that the Domain.login was called without altering its behavior. Read more on stubs/spies here if you would like - http://sinonjs.org/releases/v1.17.7/stubs/

Resources