How to assert this promise via jest? - javascript

I write a test with jest to test one of my middleware.
const asyncAll = (req, res, next) => {
const queue = [
service.exchangeLongTimeToken(req),
service.retrieveUserInfo(req),
];
Promise.all(queue).then((values) => {
res.locals.auth = values[0];
res.locals.user = values[1];
next();
}).catch((err) => {
next(err)
});
};
The test file is like this:
const httpMocks = require('node-mocks-http');
const testData = require('../../testdata/data.json');
describe('Test asyncAll', () => {
let spy1 = {};
let spy2 = {};
const mockNext = jest.fn();
afterEach(() => {
mockNext.mockReset();
spy1.mockRestore();
spy2.mockRestore();
});
test('Should call next() with no error when no error with 2 requests', () => {
spy1 = jest.spyOn(service, 'exchangeLongTimeToken').mockImplementation((url) => {
return Promise.resolve(testData.fbLongTimeToken);
});
spy2 = jest.spyOn(service, 'retrieveUserInfo').mockImplementation((url) => {
return Promise.resolve(testData.fbUserInfo);
});
const request = httpMocks.createRequest();
const response = httpMocks.createResponse();
asyncAll(request, response, mockNext);
expect(spy1).toBeCalled();
expect(spy2).toBeCalled();
expect(mockNext).toBeCalled();
expect(mockNext).toBeCalledWith();
expect(mockNext.mock.calls.length).toBe(1);
});
}
The error is like this:
Error: expect(jest.fn()).toBeCalled()
Expected mock function to have been called.
at Object.<anonymous> (tests/backend/unit/fblogin/asyncAll.test.js:39:26)
Which reflects the line:
expect(mockNext).toBeCalled();
Why it doesn't get called?
I read the documents about jest, it says I need to return the promise in order to test the value. But the asyncAll() doesn't return a promise, instead, it consumes a promise, how to deal with this?

You have to notify Jest about the promises you create in the test, have a look at the docs on this topic:
test('Should call next() with no error when no error with 2 requests', async() => {
const p1 = Promise.resolve(testData.fbLongTimeToken);
const p2 = Promise.resolve(testData.fbUserInfo);
spy1 = jest.spyOn(service, 'exchangeLongTimeToken').mockImplementation((url) => {
return p1
});
spy2 = jest.spyOn(service, 'retrieveUserInfo').mockImplementation((url) => {
return p2
});
const request = httpMocks.createRequest();
const response = httpMocks.createResponse();
asyncAll(request, response, mockNext);
await Promise.all([p1,p2])
expect(spy1).toBeCalled();
expect(spy2).toBeCalled();
expect(mockNext).toBeCalled();
expect(mockNext).toBeCalledWith();
expect(mockNext.mock.calls.length).toBe(1);
});

Related

Jest mock module.exports module and count called times

I have some dependency in the tested module.
sendResponse.js:
module.exports = function sendResponse(res, data) {
res.send(data);
};
testedModule.js:
const login = require('.../sendResponse');
exports.postLogin = withServerErrorHandler(async (req, res) => {
const { body } = req;
const { email, password } = body;
const user = await login(email, password);
return sendResponse(res, user);
});
And I want to mock sendResponse module and count its calls.
const testedModule = require('.../testedModule');
jest.mock('.../sendResponse', () => jest.fn());
const sendResponse = require('.../sendResponse');
describe('testedModule', () => {
const res = {
//..
};
const req = {
//..
};
describe('Authentication', () => {
test('should pass', async (done) => {
await testedModule.postLogin(req, res);
expect(sendResponse).toHaveBeenCalledTimes(1);
done();
});
})
});
Unfortunately, Jest always gives me that result, It looks like Jest jest spy on jest.fn() function, not specific ref. How can I deal with it?
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0

Mock class using JEST in node

So, this is actually something that happened to my several times and I never knew how to solve it.
I have a class like this
//myClass.js
const { get } = require('../models/myModel');
class MyClass {
constructor(id) {
this.id = id;
}
async getByID() {
return get(this.id);
}
}
and a controller that is
//controller.js
module.exports = MyClass;
const MyClass = require('./MyClass.js');
module.exports = {
getController: (req, res, next) => {
try {
const MyObject = new MyClass(1);
const info = await MyObject.getByID();
res.send(info)
}
catch (e) {
next(e);
}
}
}
When I want to do an E2E test, i need to mock that getByID to see what happen when it resolves and rejects.
So, how do I mock a constructor? I did something like this
// myclass.spec.js
const MyClass = require('./MyClass.js');
jest.mock('./MyClass', () => jest.fn().mockImplementation(() => ({
getByID: Promise.resolve({id: 1, name: 'John'}),
})));
describe('Testing MyClass', () => {
it('Should return info', () => {
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
})
I know this test currently works, but now I cant change that getByID mock value to see what happen when it rejects the promise.
If I try to include it inside the test (it) .... it won't mock anything...
I want something like
const MyClass = require('./MyClass.js');
const {pipelineController} = require('./controllers.js')
jest.mock('./MyClass', () => jest.fn().mockImplementation(() => ({
getInfo: jest.fn(),
})));
describe('Testing MyClass', () => {
it('Should return info', () => {
ProcessService.getInfo.mockImplementation(() => Promise.resolve('the value i want'))
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
it('Should return info', () => {
ProcessService.getInfo.mockImplementation(() => Promise.reject('the reason i want'))
const res = httpMocks.createResponse();
const mReq = httpMocks.createRequest();
const mNext = jest.fn();
const mRes = mockResponse();
await pipelineController(mReq, mRes, mNext);
expect(mRes.send).toHaveBeenCalledWith(1);
done();
})
})
Since getByID is called right after instantiation, a spy should be available outside mocked class in order to change the implementation.
This can be done with class auto-mock that works in obscure and unspecified ways but may be suitable for this specific case:
jest.mock('./MyClass.js')
const MyClass = require('./MyClass.js');
...
MyClass.prototype.getByID.mockResolvedValue({id: 1, name: 'John'});
// instantiate MyClass and call getByID
And this can be done by exposing a spy outside the class:
const mockGetByID = jest.fn();
jest.mock('../../../services/ProcessService', () => jest.fn(() => (
{ getByID: mockGetByID }
)));
const MyClass = require('./MyClass.js');
...
mockGetByID.mockResolvedValue({id: 1, name: 'John'});
// instantiate MyClass and call getByID

sinon.mock().expects().atLeast() ... expectation.verify() not working

I am newbie on node and sinon and I'm having trouble to test the component below. I would like to check if the res.status and res.send were called inside the component.
Component to be tested
module.exports = {
handle: function(promise, res, next, okHttpStatus) {
promise
.then(payload => res.status(okHttpStatus ? okHttpStatus : 200).send(payload))
.catch(exception => next(exception));
}
};
Unit test
const sinon = require("sinon");
const routerPromiseHandler =
require("../../../main/node/handler/PromiseHandler");
describe("Should handle promisse", () => {
it("should handle success promise return", () => {
const successMessage = {message: "Success"};
const promiseTest = new Promise((resolve, reject) => {
resolve(successMessage);
});
let res = {
status: function() {},
send: function() {}
};
const mockRes = sinon.mock(res);
const expectStatus = mockRes.expects("status").withExactArgs(200).atLeast(1)
const expectSend = mockRes.expects("send").withExactArgs(successMessage).atLeast(1)
const spyNext = sinon.spy();
routerPromiseHandler.handle(promiseTest, res, spyNext, 200);
expectStatus.verify();
expectSend.verify();
});
});
I managed to solve the problem. The sinon check wasn't work because the spys were been called inside a promise. To check if the spy were been called. I had to add the assertions inside the then and catch of the promises.
const sinon = require("sinon");
const { mockResponse } = require("mock-req-res");
const routerPromiseHandler = require("../../../main/node/handler/PromiseHandler");
describe("Should handle promisse", () => {
it("should handle success promise return", () => {
const successMessage = { message: "Success" };
const promiseTest = new Promise((resolve, reject) => {
resolve(successMessage);
});
const mockedRes = mockResponse();
const spyNext = {};
routerPromiseHandler.handle(promiseTest, mockedRes, spyNext, 200);
promiseTest.then(() => {
sinon.assert.calledWithMatch(mockedRes.status, 200);
sinon.assert.calledWithMatch(mockedRes.send, successMessage);
})
});
it("should handle error promise return", () => {
const errorMessage = { error: "error" };
const promiseError = new Promise((resolve, reject) => {
reject(errorMessage);
});
const mockedRes = mockResponse();
const nextSpy = sinon.spy();
routerPromiseHandler.handle(promiseError, mockedRes, nextSpy, 200);
promiseError
.then(() => {
// Promise always need the then
})
.catch(exception => {
sinon.assert.calledWithMatch(nextSpy, errorMessage);
})
});
});

Node.js - unit test for function with mocked promise inside

I'm currently making a small server in JavaScript and as part of the learning process I'm writing unit tests for the functions. Unfortunately I ran into major difficulties with a certain test that handles a promise. Below is the router module, with a separate handlePUT function for ease of testing.
const express = require('express');
const service = require('../service/user.service');
const dutyStatusRouter = express.Router();
const output = console;
function handlePUT(req, res) {
service.updateUserStatus()
.then((fulfilled) => {
res.status(fulfilled);
res.send();
})
.catch(() => {
res.status(500);
res.send();
});
}
dutyStatusRouter.route('/').put(handlePUT);
The updateUserStatus function basically toggles a Boolean in a database and looks somewhat like this:
function updateUserStatus() {
return new Promise((resolve, reject) => {
if (…) {
resolve(201);
} else if (…) {
resolve(200);
} else {
reject();
}
});
}
As for the unit tests, I'm using mocha/chai, with proxyquire to create a mock updateUserStatus.
const chai = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const serviceStub = {};
describe('=== Unit test ===', () => {
it('Handle PUT test: promise kept', async () => {
const dutyStatusRouter = proxyquire('../../router/duty-status.router', {
'../service/user.service': serviceStub,
});
serviceStub.updateUserStatus = () => {
return new Promise((resolve, reject) => {
resolve(200);
});
};
const res = {
status: sinon.fake(),
send: sinon.fake(),
};
await dutyStatusRouter.handlePUT({}, res);
chai.assert(res.status.calledOnceWith(200));
});
});
Whenever I try to run the unit test, I get the error Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.. If I try to add done() it still fails by giving the error message Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
Found a solution that works, so I'm adding it here:
const chai = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');
const serviceStub = {};
const dutyStatusRouter = proxyquire('../../router/duty-status.router', {
'../service/user.service': serviceStub,
});
describe('=== Unit test ===', () => {
it('Handle PUT test: promise kept', (done) => {
serviceStub.updateUserStatus = sinon.stub().resolves(200);
const res = {
status: sinon.fake(),
send: sinon.fake(),
};
dutyStatusRouter.handlePUT({}, res).then(() => {
chai.assert(res.status.calledWith(200));
done();
});
});
});
Note: I changed the handlePUT function just a bit, it now looks like this (I just added a return):
function handlePUT(req, res) {
return service.updateUserStatus()
.then((fulfilled) => {
output.log('Promise fulfilled');
res.status(fulfilled);
res.send();
})
.catch(() => {
output.log('Promise unfulfilled');
res.status(500);
res.send();
});
}

Unable to unit test firebase function in Sinon

I am trying to unit test a JavaScript promise, that contains a firebase query, using mocha, chai, sinon. I am trying to mock the database using sinon rather than actually making a request to the database. However, I am unable to implement it correctly.
Here is my promise in the file '/services/pr_services:
exports.getUserInfo = (userId) => {
return new Promise((resolve, reject) => {
const userProfile = {};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value', (snap) => {
if (snap.exists()) {
const userProfileData = snap.val();
resolve(userProfile);
} else {
reject();
}
});
});
};
The variable database contains the database configuration like credentials, database url, etc
Here is my code for the test case:
const chai = require('chai');
const sinon = require('sinon');
const admin = require('firebase-admin');
const database = require('../database');
const services = require('../services/pr_services');
const should = chai.should();
describe('Database functions', () => {
let adminInitStub;
before(() => {
adminInitStub = sinon.stub(admin, 'initializeApp');
});
describe('get profile info', () => {
it('should return a non empty object', (done) => {
beforeEach(() => {
services.getUserInfo = sinon.stub();
});
afterEach(() => {
services.getUserInfo.reset();
});
const userId = 'jim123';
const snap = {
name: 'Jim Dani',
address: 'Porto'
};
const userProfileRef = database.ref('profiles').child(userId);
userProfileRef.once('value').returns(Promise.resolve(snap));
services.getUserInfo
.then(info => {
info.should.be.a('object');
info.should.equal(snap);
done();
})
.catch(err => {
should.not.exist(err);
done();
});
});
});
after(() => {
adminInitStub.restore();
test.cleanup();
});
});
Can anyone point out where I am going wrong and kindly point me in right direction.
Thanks.

Categories