My function is:
const PhoneNumber = require('awesome-phonenumber');
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const { twiml } = require('twilio');
exports.incoming = (requestBody) => {
const MessagingResponse = twiml.MessagingResponse;
const VoiceResponse = twiml.VoiceResponse;
const pn = new PhoneNumber(requestBody.Body, 'US');
return global.db.Conference.create({})
.then((dbCreate) => {
conferenceId = dbCreate.id;
// Call originator
return twilio.calls.create({
to: requestBody.From,
from: requestBody.To,
url: `${process.env.API_URL}/calls/conference?id=${conferenceId}`
});
})
My test is:
const _ = require('lodash');
const sinon = require('sinon');
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
const { twiml } = require('twilio');
const SmsController = require('../../../controllers/sms');
const twilioIncomingSmsReq = require('../../mocks/twilioIncomingSmsReq');
describe.only('Sms Controller', () => {
let messagingResponseMessageStub;
let conferenceCreateStub;
beforeEach(() => {
messagingResponseMessageStub = sinon.stub(twiml.MessagingResponse.prototype, 'message').returns(true);
conferenceCreateStub = sinon.stub(global.db.Conference, 'create').resolves({ id: 1 });
return;
});
afterEach(() => {
messagingResponseMessageStub.restore();
conferenceCreateStub.restore();
return;
});
it.only('should call the originator and recipient', () => {
let requestBody = _.clone(twilioIncomingSmsReq);
console.log(twilio.calls);
let twilioDialStub = sinon.stub(twilio.calls, 'create').resolves(true);
console.log(twilio.calls);
return SmsController.incoming(requestBody)
.then(() => {
sinon.assert.calledWith(twilioDialStub, {
to: requestBody.From,
from: requestBody.To,
url: `${process.env.API_URL}/calls?id=1`
});
sinon.assert.calledWith(twilioDialStub, {
to: '+number',
from: requestBody.From,
url: `${process.env.API_URL}/calls?id=1`
});
twilioDialStub.restore();
return;
});
});
});
But in my code, the actual twilion.calls.create function is called. Not the stub. What am I doing wrong?
twilio refer to different objects in these modules. require('twilio') is factory function. It's expected to create a new object even when it's called with same arguments, require('twilio')(...) !== require('twilio')(...).
Consider exporting twilio instance, at least for testing purposes:
const twilio = require('twilio')(process.env.TWILIO_ACCOUNT_SID,
...
exports.twilio = twilio;
Related
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)
This is the unit test in question:
const chai = require('chai');
const sinonChai = require('sinon-chai');
const sinon = require('sinon');
const appRefAPI = require('../../../../../../app/services/api');
const { getMarkAsManual, submitMarkAsManual } = require('../../../../../../app/services/handler/manual/mark-as-manual-handler');
const appRefResult = JSON.parse(JSON.stringify(require('../../../response/application-received-full')));
const postData = JSON.parse(JSON.stringify(require('../../../response/post-app')));
const offerAccepted = JSON.parse(JSON.stringify(require('../../../response/exception-offer-accepted')));
const { expect } = chai;
chai.use(sinonChai);
describe('details/mark-as-manual-handler.js', () => {
let req;
let res;
let sandbox;
describe.only('submitMarkAsManual()', async () => {
before(() => {
res = {
render: () => ({})
};
req = {
session: {}
};
sandbox = sinon.createSandbox();
});
beforeEach(() => {
sandbox.stub(res, 'render').returns({});
sandbox.stub(appRefAPI, 'postClose').returns([200, postData]);
});
afterEach(() => {
sandbox.restore();
});
it('should render-manually-process-confirmation', () => {
req.session.application_reference = 'EZ123456';
req.session.data = offerAccepted
req.body = {
'manually-processed-day': '3',
'manually-processed-month': '3',
'manually-processed-year': '1999'
}
res.locals = {};
res.locals.application_reference = req.session.application_reference;
submitMarkAsManual(req, res);
console.log(res.render)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
});
});
});
This is the code it's looking at:
const submitMarkAsManual = async (req, res) => {
const errors = [];
let dd = req.body['manually-processed-day'];
let mm = req.body['manually-processed-month'];
let yyyy = req.body['manually-processed-year'];
if (dd.length === 1) dd = '0'+dd;
if (mm.length === 1) mm = '0'+mm;
const credit_date = `${dd}/${mm}/${yyyy}`
res.locals = req.session.data;
res.locals.credit_date = credit_date;
if (util.isValidDate(credit_date) === false) {
errors.push('date-invalid');
res.render('pages/mark-as-manual', { errors });
} else {
let data = {
"application_id": req.session.application_reference,
"closure_reason": "offer_response_processed_manually",
"credit_date": credit_date
}
const response = await callAPI.postClose(data);
if (response[0] === 200 && response[1].status === 'SUCCESS') {
console.log('success!!!!')
res.render('pages/manually-process-confirmation');
}else{
res.redirect('/budgeting-loans-ui/problem-with-service');
}
}
};
And from this I get the following message:
1) details/mark-as-manual-handler.js
submitMarkAsManual()
should render-manually-process-confirmation:
AssertionError: expected render to have been called exactly once with arguments pages/manually-process-confirmation
at Context.<anonymous> (test/unit/app/services/handler/manual/mark-as-manual-handler-test.js:85:45)
at processImmediate (node:internal/timers:463:21)
In the code being tested, just before the render, I've put in a console.log call that outputs 'success!!!!'. When I run the test, this pops out so I know it reaches (and presumably executes) the render.
Any suggestions?
Move the following lines to an asynchronous function:
submitMarkAsManual(req, res);
console.log(res.render)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
with a call to an asynchronous function:
const sub = async (req, res) => {
await submitMarkAsManual(req, res)
expect(res.render).to.have.been.calledOnceWith('pages/manually-process-confirmation');
}
(Thanks to IAmDranged for pointing the way.)
I have developed the node js code as MVC architecture. The folder structure is Controller --> service -> model. And I have tried to write unit testing for the following code. Unfortunately, I couldn't mock the service function. So please help me to resolve it.
Controller
const SubscriberService = require('../../services/subscriber/subscriberService')
const response = require("../../config/response");
const constant = require('../../config/constant');
const SubscriberAnswerService = require('../../services/subscriberAnswer/subscriberAnswerService');
const path = require('path');
class SubscriberController {
constructor() {
this.subscriberService = new SubscriberService();
this.subscriberAnswerService = new SubscriberAnswerService();
}
async getSubscriber(req, res) {
try {
var { userId } = req;
const user = await this.subscriberService.findByUserId(userId);
if (user != null) {
res.send(response.res(true, constant.MSG.USER_DETAILS, user))
} else {
res.status(404).send(response.res(false, constant.MSG.USER_NOT_FOUND));
}
} catch (error) {
res.status(constant.RESPONSE.INTERNAL_ERROR.CODE)
.send(response.res(false, error.message))
}
}
}
Service
async findByUserId(id) {
const user = await Subscriber.findOne({ where: { id: id, status: 1 } });
return user;
}
Unit Testing Code
describe("Test SubscriberController", () => {
it("Test getsubscriber", async () => {
req.userId = 1;
jest.spyOn(subscriberService, "findByUserId").mockReturnValue(subscriberResponse);
await subscriberController.getSubscriber(req, res);
expect(res.statusCode).toBe(500);
});
});
Issue: I have mocked the service function which findByUserId but it does not work. It is given the following error.
error TypeError: Cannot read property 'findOne' of undefined
Please give the solution to mock findByUserId function.
Subscriber.Controller.test.js
const subscriberModel = require("../src/models/subscriber/subscriberModel");
const SubscriberService = require("../src/services/subscriber/subscriberService");
const SubscriberController = require("../src/controllers/Subscriber/subscriberController");
const subscriberController = new SubscriberController();
const subscriberService = new SubscriberService();
const httpMocks = require("node-mocks-http");
jest.mock("../src/models/subscriber/subscriberModel");
beforeEach(() => {
jest.resetAllMocks();
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn();
jest.resetAllMocks();
subscriberModel.findOne = jest.fn();
});
// subscriberService.findByUserId = jest.fn();
const subscriberResponse = {
id: 1,
name: 'Sandun',
msisdn: '94704377575',
otp: '1234',
deleted: 0,
attempts: 0,
img_url: 'https://'
}
jest.mock('../src/models/subscriber/subscriberModel', () => () => {
const SequelizeMock = require("sequelize-mock");
let dbMock = new SequelizeMock();
let subscriberMock = dbMock.define('subscribers', {
id: 1,
name: 'Sandun',
msisdn: '94704377575',
otp: '1234',
deleted: 0,
attempts: 0,
img_url: 'https://'
});
let groupMock = dbMock.define('winner', {});
subscriberMock.belongsTo(groupMock);
subscriberMock.hasMany();
});
// This test shows how the constructor can be mocked, and how to spy on passed parameters.
describe("Test SubscriberController", () => {
it("Test getsubscriber", async () => {
req.userId = 1;
jest.spyOn(subscriberService, "findByUserId").mockReturnValue(subscriberResponse);
await subscriberController.getSubscriber(req, res);
expect(res.statusCode).toBe(200);
});
});
I have a code in Controller
const getLocalDatabaseResule = async (searchURL, reqBody) => {
commonCode(reqBody);
console.log(name);
});
function commonCode(reqBody) {
var name = reqBody.name;
var phone= reqBody.phone;
var email = reqBody.email;
}
Any idea how to add common function in controller
You need to return the body from commonCode
const getLocalDatabaseResule = async (searchURL, reqBody) => {
const {name,phone,email} = commonCode(reqBody);
console.log(name);
};
function commonCode(reqBody) {
const name = reqBody.name;
const phone= reqBody.phone;
const email = reqBody.email;
return {name,phone,email}
}
getLocalDatabaseResule("searchURL", {name:"User",phone:"111", email:"mail#mail.com"})
Also this is possible
const getLocalDatabaseResule = async (searchURL, reqBody) => {
var x = commonCode(reqBody);
console.log(x.name);
});
function commonCode(reqBody) {
this.name = reqBody.name;
this.phone= reqBody.phone;
this.email = reqBody.email;
}
I am attempting to retrieve the boolean child (notificationsOn) of an object stored as a Firestore document to see if the rest of a function should be executed.
The overall function works to completion without this portion, but adding the portion from let threadDoc to the if statement presents a "threadDoc.get is not a function" error. I think my syntax is wrong but I don't know how, as a similar function works in a later part of the function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendDMNotification =functions.firestore.document('/dm_threads/{thread_id}/messages/{message_id}').onCreate((snapshot, context) => {
const newMessage = snapshot.data();
const senderName = newMessage.authorName;
const senderID = newMessage.authorUID;
const messageText = newMessage.message;
const recipientID = newMessage.recipientUID;
var notificationsOn = null;
let deviceTokenQuery = admin.firestore().collection(`/users/${recipientID}/device_tokens/`);
var idsToBeSorted = [senderID, recipientID];
idsToBeSorted.sort();
var threadID = idsToBeSorted[0] + idsToBeSorted[1];
console.log(recipientID);
console.log(threadID);
let threadDoc = admin.firestore().document(`users/${recipientID}/threads/${threadID}/`);
return threadDoc.get().then(doc => {
let notificationsOn = doc.data.notificationsOn;
console.log(notificationsOn);
if (notificationsOn !== false){
return deviceTokenQuery.get().then(querySnapshot => {
let tokenShapshot = querySnapshot.docs;
const notificationPromises = tokenShapshot.map(doc => {
let token_id = doc.data().tokenID;
const payload = {
data: {
title: senderName,
body: messageText,
senderID: senderID,
senderName: senderName
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log("Notification sent: ", response);
})
.catch(error => {
console.log("Error sending message: ", error);
});
});
return Promise.all(notificationPromises);
});
}
return;
});
});
admin.firestore().document() was supposed to be admin.firestore().collection(...).doc(...)
This fixed my problem
I think you meant to say admin.firestore() instead of functions.firestore.