JS/Jest: mocking a class to check if method called - javascript

I am trying to test the loading of a player in my main js file. It just created a new instance of an IVSPlayer class and then calls init() on it.
MAIN.js
const ivsPlayer = new IVSPlayer({
id: VIDEO_PLAYER_ID,
config: VIDEO_JS_CONFIG,
ivsTech: win.registerIVSTech,
});
ivsPlayer.init();
player = ivsPlayer.player;
I am trying to mock the implementation below
MAIN.test.js
import IVSPlayer from './ivs-player';
it('should load the player', async () => {
const mockInit = () => jest.fn();
jest.mock('./ivs-player', () => {
return {
init: mockInit,
};
});
await createPlayers();
expect(?????).toHaveBeenCalled();
});
What do I put in for the expect to listen to since I mocked the ivs-player

I think your mocking is wrong. Try this (mock on the prototype)
it('should load the player', async () => {
IVSPlayer.prototype.init = jest.fn().mockImplementation(() => {
return {
init: jest.fn(),
};
});
// execute code that causes the init function to run
expect(IVSPlayer.prototype.init).toHaveBeenCalled();
});
Alternatively, you can try this
import { IVSPlayer } from './ivs-player';
jest.mock('./ivs-player'); // Automatic mock of the file
it('should load the player', async () => {
// execute code that causes the init function to run
const mockInit = IVSPlayer.mock.instances[0].init;
expect(mockInit).toHaveBeenCalled();
});
You can read more on class mocks here.

Related

Jest spy not working while testing a function within a function

I'm trying to test a function in a class-based entity and trying to spy on another function that is being called inside the one I'm testing. But even though the child function is being called once, Jest fails to spy on it and says it was called 0 times.
Let's say this is my class:
class Example {
function1() {
const data = this.function2();
return data;
}
function2() {
...some code
}
}
So to test function1, I'm doing this:
describe('ExampleComponent', () => {
beforeEach(() => {
client = new Example();
});
it('should call function2 once', async() => {
const callFunction1 = client.function1();
const function2Spy = jest.spyOn(client, 'function2');
expect(function2Spy).toHaveBeenCalledTimes(1);
});
});
What am I missing here?
You are calling function first then spying on another function.
Try spying before function call
describe('ExampleComponent', () => {
beforeEach(() => {
client = new Example();
});
it('should call function2 once', async() => {
const function2Spy = jest.spyOn(client, 'function2');
client.function1();
expect(function2Spy).toHaveBeenCalledTimes(1);
});
});

Why isn't my jest mock function implementation being called?

I have the following jest test configuration for my collection of AWS JS Node Lambdas. I have a module called dynamoStore I reference in several different lambdas package.json and use within the lambdas. I am trying to get test one of these lambdas by mocking the dynamo store module as it makes calls to dynamoDb. The problem is that the jest.fn implementation never gets called. I confirmed this by sticking a breakpoint in that line as well as logging the value the calling methods returns from it.
When I check lambda1/index.js in the debugger getVehicleMetaKeysFromDeviceId() is a jest object but when it is called it doesn't use my mock implementation
How do I get this implementation to work? Have I set up my mock incorrectly?
dynamoStore/vehicleMetaConstraints
exports.getVehicleMetaKeysFromDeviceId= async (data) => {
return data
};
dynamoStore/index.js
exports.vehicleMetaConstraints = require("./vehicleMetaConstraints");
...
lambda1/index.js
const { vehicleMetaStore } = require("dynamo-store");
exports.handler = async (event, context, callback) => {
const message = event;
let vehicle_ids = await vehicleMetaStore.getVehicleMetaKeysFromDeviceId(message.id);
// vehicle_ids end up undefined when running the test
}
lambda1/index.test.js
const { vehicleMetaStore } = require("dynamo-store");
jest.mock("dynamo-store", () => {
return {
vehicleMetaStore: {
getVehicleMetaKeysFromDeviceId: jest.fn(),
},
};
});
describe("VehicleStorageLambda", () => {
beforeEach(() => {
jest.resetModules();
process.env = { ...env };
});
afterEach(() => {
jest.clearAllMocks();
});
test("Handles first time publish with existing device", async () => {
let functionHandler = require("./index");
vehicleMetaStore.getVehicleMetaKeysFromDeviceId.mockImplementationOnce(() =>
// This never gets called
Promise.resolve({
device_id: "333936303238510e00210022",
})
);
await functionHandler.handler({});
});
});
Remove the call to jest.resetModules() in beforeEach. That's re-importing your modules before each test, and wiping out your mocks.
https://stackoverflow.com/a/59792748/3084820

Jest: Vue Component can't find mocked function

I'm mocking a ES6 class which is used inside my Vue Component:
export default class DataUploadApi {
// Get uploaded files
static async getUploadedFiles() : Promise<Object> {
return WebapiBase.getAsync({uri: DATA_UPLOAD_ENPOINTS.FILES});
}
}
I've been following along with this document, but I think my syntax is slightly off with my mock:
import { mount } from '#vue/test-utils';
import DataUploadApi from '../webapi/DataUploadService';
import FileDownloadList from '../components/file-download-list.vue';
const mockGetUploadedFiles = jest.fn().mockResolvedValue({json: JSON.stringify(uploadedFilesObj)});
jest.mock('../webapi/DataUploadService', () => jest.fn().mockImplementation(() => ({getUploadedFiles: mockGetUploadedFiles})));
describe('file-download-list component', () => {
beforeEach(() => {
// #ts-ignore
DataUploadApi.mockClear(); // https://stackoverflow.com/a/52707663/1695437 dont use # imports on mocks.
mockGetUploadedFiles.mockClear();
});
describe('renders correct markup:', () => {
it('without any uploaded files', () => {
const wrapper = mount(FileDownloadList, {});
expect(wrapper).toMatchSnapshot();
});
});
});
This test passes. However, in the snapshot I can see that the API called failed with this error message:
<p>
_DataUploadService.default.getUploadedFiles is not a function
</p>
What have I done wrong with the function mock? Thanks in advance!
There seemed to be a few issues with my code:
Mocking the API
Using an internal mockImplementation seems to cause issues, and is not required if you don't need the additional mock functionality.
jest.mock('#/apps/gb-data/webapi/DataUploadService', () => ({
getUploadedFiles() {
return Promise.resolve({ uploaded_files: {} });
},
}));
Changes to the test
Both flushPromises and nextTick are required.
it('with uploaded files', async () => {
const wrapper = mount(FileDownloadList, {
stubs: fileDownloadListStubs,
});
await flushPromises();
await wrapper.vm.$nextTick();
expect(wrapper).toMatchSnapshot();
});

Several mocks of one module in one file with jest

I have to functions for example a, b. Both of them are elements of one module, and they are re-exported in index.js. Function a invokes function b.
It all works if i use jest.mock on the top of the file, but if i want to specify different mock implementation of b function in every it block it doesn't work. Also i try'ed to use jest.doMock but it doesn't work as well.
a.js
import * as fromDependencies from '.'
export function(arg) {
return !fromDependencies && !arg;
}
b.js
export function b() {
//some code
return boolean;
}
index.js
export * from 'a.js',
export * from 'b.js'
testFile
import a from '../a.js';
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
jest.mock('../.', () => ({
b: jest.fn(() => true);
}))
expect(a(true)).toBe(false);
});
it('should return true', function() {
jest.mock('../.', () => ({
b: jest.fn(() => false);
}))
expect(a(false)).toBe(false);
});
});
The results are fake, anyway i want to call my mocked function not the original one. When i have jest.mock in the top of the file it works but i can achieve just one mock for on file. Do mock does't work. I'll be really grateful if somebody can provide some example how i can resolve that ;).
You could use a spy instead of a mock. Since a exports a function instead of an object then you'd have to wrap it.
import a from '../a.js'
const aWrapped = { a }
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
const mockA = jest.fn()
const spyedA jest.spyOn(aWrapped, a).mockReturnValue(mockA)
expect(spyedA(true)).toBe(false);
});
});
Something like that.

Jest mock class method on a test-by-test basis

I'm trying to mock out a utility library class with a method that returns a JSON.
Actual library structure
module.exports = class Common() {
getConfig() {
return {
real: 'data'
}
}
The file under test looks like:
const Common = require('./common');
const common = new Common();
const config = common.getConfig();
...
const someFunction = function() {
// config.real is used inside this function
}
I'm trying to mock out the Common class and return a different config JSON for each Jest test.
const fileUnderTest = require('./../fileUnderTest.js');
const Common = require('./../common.js');
jest.mock('./../common.js');
describe('something', () => {
it('test one', () => {
Common.getConfig = jest.fn().mockImplementation(() => {
return {
real : 'fake' // This should be returned for test one
};
});
fileUnderTest.someFunction(); //config.real is undefined at this point
});
it('test two', () => {
Common.getConfig = jest.fn().mockImplementation(() => {
return {
real : 'fake2' // This should be returned for test two
};
});
})
})
Is it possible to set the return value from the mock class method created by the automock of common.js at the top of the test file?
I've tried to use mockReturnValueOnce() etc.
jest.mock
In this case you don't really need to auto-mock the entire common module since you are just replacing the implementation of one method so jest.mock('./../common'); isn't necessary.
Common.getConfig
getConfig is a prototype method so getConfig exists on the prototype of Common. To mock it use Common.prototype.getConfig instead of Common.getConfig.
config in fileUnderTest.js
An instance of Common gets created and config gets set to the result of calling common.getConfig() as soon as fileUnderTest runs, which happens as soon as it gets required so the mock for Common.prototype.getConfig has to be in place before you call require('./../fileUnderTest').
const Common = require('./../common');
Common.prototype.getConfig = jest.fn().mockImplementation(() => ({ real: 'fake' }));
const fileUnderTest = require('./../fileUnderTest');
describe('something', () => {
it('should test something', () => {
fileUnderTest.someFunction(); // config.real is 'fake' at this point
});
});
Update
To mock config.real differently for each test for code like this requires that the modules be reset between tests:
describe('something', () => {
afterEach(() => {
jest.resetModules(); // reset modules after each test
})
it('test one', () => {
const Common = require('./../common');
Common.prototype.getConfig = jest.fn().mockImplementation(() => ({ real: 'fake' }));
const fileUnderTest = require('./../fileUnderTest');
fileUnderTest.someFunction(); // config.real is 'fake'
});
it('test two', () => {
const Common = require('./../common');
Common.prototype.getConfig = jest.fn().mockImplementation(() => ({ real: 'fake2' }));
const fileUnderTest = require('./../fileUnderTest');
fileUnderTest.someFunction(); // config.real is 'fake2'
})
})
Resetting the modules is necessary because once a module is required it is added to the module cache and that same module gets returned each time it is required unless the modules are reset.

Categories

Resources