Jest, How to mock an imported callback in Object Destructuring? - javascript

I did the tests in the following way, but it bothers me how I need to import an object with the functions, instead of just importing the functions.
this works
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const get = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await get.getRegisteredUser(depositName, depositCpf);
}
this dosent
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser = jest.fn()
.mockImplementationOnce(async () => mock)
.mockImplementationOnce(async () => mockUpdated);
//service.js
const { getRegisteredUser } = require('../model/getRegisteredUser');
const testedFunction () => {
const depositReciver = await getRegisteredUser(depositName, depositCpf);
}
I'm triyng this (way):
//service.test.js
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
jest returns this:
TypeError: get.mockImplementationOnce is not a function
also I've tried to import like this
//service.test.js
const { getRegisteredUser } = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
getRegisteredUser.mockImplementationOnce(() => ({ getRegisteredUser: () => mockObject }));
EDIT
//getRegisteredUser.js
const { getConnection } = require('../../../global/connection');
const getRegisteredUser = async (userName, cpf) => {
const db = await getConnection('Data-Base');
const res = await db.collection('Collection')
.findOne({ userName, cpf });
return res;
};
module.exports = { getRegisteredUser };

The example you are pointing [https://jestjs.io/docs/es6-class-mocks#replacing-the-mock-using-mockimplementation-or-mockimplementationonce][1]
is related to the default export but I think you are using named export in your code.
There are multiple ways to do so
One Way
const get = require('../../modules/bankUser/model/getRegisteredUser');
jest.mock('../../modules/bankUser/model/getRegisteredUser');
get.getRegisteredUser.mockImplementationOnce(() => mockObject);
Second Way
jest.mock('../../modules/bankUser/model/getRegisteredUser', () => ({
...jest.requireActual('../../modules/bankUser/model/getRegisteredUser'),
getRegisteredUser: jest.fn().mockImplementation(() => mockObject),
}))
An Another Way
We can also mock a module using the __mocks__ directory which is inbuilt feature of jest
And at the end never forgot to call clearAllMocks() after each test else it may impact the output of other tests.
afterEach(() => {
jest.clearAllMocks();
});

Related

How do I implement a unit test for a function that invokes two other functions

Inside this module, I want to test the getAppts function, which invokes two function. What is the correct way of evaluating the code that getAppts encompasses? Do I have to run db.getDatabase() and fetchAppts() as stubs inside the unit test function? My unit test implementation is, at best, incomplete, but it could be totally wrong.
'use strict'
const db = require('../db/db')
const log = require('../utils/logging')(__filename)
const { fetchAppts, fetchApptDetails } = require('../db/dao/appointment-dao')
async function getAppts (queryParams) {
log.debug('getAppts(): %j', queryParams)
const knex = db.getDatabase()
const appointments = await fetchAppts(queryParams, knex)
return appointments
}
async function getApptDetails (demoApptId) {
log.debug('getApptDetails(): %j', demoApptId)
const knex = db.getDatabase()
const apptDetails = await fetchApptDetails(demoApptId, knex)
return apptDetails
}
module.exports = {
getAppts,
getApptDetails
}
unit test setup:
'use strict'
const sinon = require('sinon')
const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const dirtyChai = require('dirty-chai')
const sinonChai = require('sinon-chai')
const { expect } = require('chai')
const proxyquire = require('proxyquire')
const apptsResponse = require('../../data/get-appts-response.json')
chai.use(dirtyChai)
chai.use(sinonChai)
chai.use(chaiAsPromised)
chai.should()
describe.only('appointment-service.js', () => {
let apptService
let apptDaoStub
let dbStub
beforeEach(() => {
dbStub = {
getDatabase: sinon.stub()
}
apptDaoStub = {
fetchAppts: sinon.stub()
}
apptService = proxyquire('../../../services/appointment-service', {
'../db/db': dbStub,
'../db/dao/appointment-dao': apptDaoStub
})
})
afterEach(() => sinon.restore())
describe('getAppts', () => {
it.only('should get appointments', async () => {
const appts = await apptService.getAppts({})
dbStub.getDatabase.should.have.been.calledTwice
})
})
})
You can use mock functions, you will need to import the module:
it('should get appointments', async () => {
const mockFn = jest.spyOn(db, 'getDatabase');
const appts = await apptService.getAppts({})
expect(mockFn).toBeCalledTimes(1); // depends on how many times fn is called
});
You can learn more about mocking here:
https://www.chakshunyu.com/blog/how-to-mock-only-one-function-from-a-module-in-jest/

Expect mocked library function to have been called

I am mocking a function in a library (auth0), this way:
jest.mock('#auth0/auth0-react', () => ({
useAuth0: () => {
return {
logout: jest.fn(),
}
}
}));
During my test, how can I expect the logout function to have been called?
Since you are trying to manually mock a node_module, you need to create a mock module file. Manual mocks are defined by writing a module in a mocks/ subdirectory immediately adjacent to the module. In your case the mock should be placed in the __mocks__ directory adjacent to node_modules.
root/__mocks__/#auth0/auth0-react.js
'use strict';
const handleRedirectCallback = jest.fn(() => ({ appState: {} }));
const buildLogoutUrl = jest.fn();
const buildAuthorizeUrl = jest.fn();
const checkSession = jest.fn();
const getTokenSilently = jest.fn();
const getTokenWithPopup = jest.fn();
const getUser = jest.fn();
const getIdTokenClaims = jest.fn();
const isAuthenticated = jest.fn(() => false);
const loginWithPopup = jest.fn();
const loginWithRedirect = jest.fn();
const logout = jest.fn();
export const Auth0Client = jest.fn(() => {
return {
buildAuthorizeUrl,
buildLogoutUrl,
checkSession,
handleRedirectCallback,
getTokenSilently,
getTokenWithPopup,
getUser,
getIdTokenClaims,
isAuthenticated,
loginWithPopup,
loginWithRedirect,
logout,
};
});
test.ts (in case you are writing test in typescript)
import { mocked } from 'ts-jest/utils';
const clientMock = mocked(new Auth0Client({ client_id: '', domain: '' }));
describe('auth0 test', () => {
it('should check logout is called', async () => {
await my_logout_function();
expect(clientMock.logout).toHaveBeenCalled();
});
});
test.js (in case you are writing tests in javascript)
const clientMock = new Auth0Client({ client_id: '', domain: '' });
describe('auth0 test', () => {
it('should check logout is called', async () => {
await my_logout_function();
expect(clientMock.logout).toHaveBeenCalled();
});
});

Jest mock not allowing inner function to be resolved in individual test

I'm testing a cloud function named myCloudFn in my functions/send.js module. My tests are in functions/test/send.test.js:
// send.js
const { getCompareDate } = require('../utils.js');
async function myCloudFn(myTestDate) {
const compareDate = await getCompareDate(argToTest);
const isOlder = myTestDate < compareDate;
return isOlder ? 'older' : 'newer';
}
module.exports = { myCloudFn };
// send.test.js
const send = require('../send.js');
jest.mock('../utils', () => ({
getCompareDate: jest.fn(() => new Date('2020-01-31')) // default
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02')),
}));
describe('send.js', () => {
it('returns date comparison from myCloudFn()', async () => {
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
});
The test functions correctly and passes as expected when I mock getCompareDate in this way, but for flexibility, I would rather provide custom input values for getCompareDate inside my tests and not 'globally'. Here's what I've tried:
const mockGetCompareDate = jest.fn();
jest.mock('../utils', () => ({
getCompareDate: mockGetCompareDate,
}));
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
const myTestDate = '2020-03-03';
const returnValues = ['older', 'newer'];
const responsePromises = returnValues.map(() => send.myCloudFn(myTestDate));
const responses = await Promise.all(responsePromises);
expect(responses[0]).toBe(returnValues[0]);
expect(responses[1]).toBe(returnValues[1]);
});
This method, however, is not working and throws an error:
ReferenceError: Cannot access 'mockGetCompareDate' before initialization
I've used this method with other tests as noted in the solution in this question, but I am not seeing similar results here. What am I missing?
Jest is hoisting the mocked function to the top of the module, and hence throws this error. The mock should instead be used right before you run the test. Further reading.
Try this:
const { getCompareDate } = require('../utils.js');
const mockGetCompareDate = jest.fn(() => new Date('2020-01-31'));
jest.mock('../utils.js', () => ({
__esModule: true,
getCompareDate: jest.fn(),
default: jest.fn()
}));
beforeAll(() => {
getCompareDate.mockImplementation(mockGetCompareDate);
});
To provide custom values do as you did before, when initialising the mock function. Source
Like this:
const mockGetCompareDate = jest.fn()
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));
Or do as you did before inside the test. Source
Like this:
it('returns date comparison from myCloudFn()', async () => {
mockGetCompareDate
.mockResolvedValueOnce(new Date('2020-04-04'))
.mockResolvedValueOnce(new Date('2020-02-02'));

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

Jest mocking a module

I'm trying to mock a module import using Jest and I'm struggling for some reason. I've got the following code:
src/elastic.js
const getRolesFunc = elasticClient => async username => {
// Do some stuff
}
module.exports = { getRolesFunc };
src/handlerFactory.js
const { getRolesFunc } = require("../src/elastic");
const handlerFactory = elasticClient =>
async (event) => {
const getRolesAsync = getRolesFunc(elasticClient);
const roles = await getRolesAsync();
}
}
My test file currently looks like:
tests/handlerFactory.unit.test.js
const { handlerFactory } = require("../src/handlerFactory");
const { getRolesFunc } = require("../src/elastic");
jest.mock("../src/elastic", () => ({
getRolesFunc: jest.fn(),
}));
describe("handlerFactory", () => {
it("handler returns correct response", async () => {
getRolesFunc.mockImplementation(() => "foo");
// Call the handler to get our actual result
const handlerAsync = handlerFactory({});
const result = await handlerAsync(event);
});
});
At the moment however I'm getting an error in my test:
TypeError: getRolesFunc.mockImplementation is not a function
I've tried a few things none of which worked, this feels like the closest but I can't work out why the jest.mock isn't working correctly. I've looked at a few examples and still can't work out why this I can't get mocking working. Can anyone help point out what I've done wrong?
As you have module.exports = { getRolesFunc }; you need to below change in your code:
const { handlerFactory } = require("../src/handlerFactory");
const elasticObj = require("../src/elastic");
jest.mock("..src/elastic");
// in your example, now put below code:
elasticObj.getRolesFunc.mockImplementation(() => "foo");

Categories