Expect mocked library function to have been called - javascript

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();
});
});

Related

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

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();
});

Mock sequelize with Jest and sequelize-mock

I'm building a new project and I'm trying to use TDD as my default methodology and trying to apply it with integration test.
The thing is easy I want to retrieve all the users from my DB.
//controller.js
const UserService = require('../services/user');
module.exports = {
// corresponds to /users
getAllUsers: async (req, res, next) => {
const users = await UserService.fetchAllUsers();
res.send(users);
},
};
const models = require('../database/models/index'); // I tried also with destructuring
module.exports = {
fetchAllUsers: async () => models.user.findAll(),
};
and my actual test file looks like
const request = require('supertest');
const SequelizeMock = require('sequelize-mock');
const app = require('..');
const dbMock = new SequelizeMock();
const models = require('../src/database/models/index');
jest.mock('../src/database/models/index', () => ({
user: jest.fn(),
}));
models.user.mockImplementation(() => {
const UserMock = dbMock.define('user', {});
UserMock.$queueResult(UserMock.build({
username: 'username',
mail: 'myMail#myMail.com',
}));
return UserMock;
});
describe('Demo test', () => {
it('should respond to users route', (done) => {
request(app)
.get('/users')
.end((err, res) => {
expect(err).toBeNull();
expect(res.status).toBe(200);
expect(res.json).toBe('object');
done();
});
});
});
All of this is actually working but when I'm trying to mock the user I TypeError: models.user.findAll is not a function
I need to replace the model.user with the UserMock's value.
Is there anyway to do this? or I should just mock findAll like
jest.mock('../src/database/models/index', () => ({
user: () => ({ findAll: jest.fn() }),
}));
``
In this case, I was not mocking what I wanted to.
If you wish to mock the models all you need to do is use SpyOn
like
const { sequelize, Report, User } = require('../../database/models/index');
describe("my test", () => {
it("should just mock", () => {
jest.SpyOn(Report, "count").mockResolvedOnce(6);
})
})
In the case that you need to mock like your service
first create jest functions to allow the access from outside the scope of your mock
and then mock it with "double class call"
const BaseService = require('../BaseService');
const mockFecthOne = jest.fn();
jest.mock('../BaseService',
() => jest.fn().mockImplementation(
() => ({
fetchOne: mockFetchOne,
}),
));
// inside your test just
BaseService().fetchOne.mockResolvedOnce({....})
and thats it hope it works.

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

How to undo mocked require in jest?

I am mocking a library by doing this:
let helperFn;
let mock;
beforeEach(() => {
mock = jest.fn();
require('./helperFn').default = mock;
})
If I do this in a test, does it mean that from now on within the whole test suite that default function of helperFn will be associated with that mock?
In the Jest documentations I see how to reset the mock, but I don't see how to remove the mock from a required function. I am concerned that from that test on, all the calls into helperFn.default will see that mock.
ES6 modules
Here is an ES6 example:
helperFn.js
export default () => 'original';
code.js
import helperFn from './helperFn';
export const func = () => helperFn();
code.test.js
import * as helperFnModule from './helperFn';
import { func } from './code';
describe('helperFn mocked', () => {
let mock;
beforeEach(() => {
mock = jest.spyOn(helperFnModule, 'default');
mock.mockReturnValue('mocked');
});
afterEach(() => {
mock.mockRestore();
});
test('func', () => {
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
expect(func()).toBe('original'); // Success!
});
});
Details
Since ES6 imports are live views of the module exports, it is easy to mock an export and then restore it afterwards.
Node.js modules
Here is a Node.js example:
helperFn.js
exports.default = () => 'original';
code.js
const helperFn = require('./helperFn').default;
exports.func = () => helperFn();
code.test.js
describe('helperFn mocked', () => {
beforeEach(() => {
const helperFnModule = require('./helperFn');
helperFnModule.default = jest.fn(() => 'mocked');
});
afterEach(() => {
jest.resetModules();
});
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('mocked'); // Success!
});
});
describe('helperFn not mocked', () => {
test('func', () => {
const { func } = require('./code');
expect(func()).toBe('original'); // Success!
});
});
Details
The default export gets remembered by code.js when it runs, so changing the default export of helperFn.js doesn't affect func once code.js is required. Jest also caches modules and returns the same module for multiple require calls unless jest.resetModules is called.
So for Node.js modules it is often easiest to require code within the test itself and use jest.resetModules to reset any mocking.

Categories