Trouble mocking a function in a module - javascript

I'm trying to create a simple test for a module. I got some firestore triggers in a module (see the module file down below). In the onDelete trigger I want to just test to see if the deleteColletion is called. To do that I need to mock out just the deleteCollection function. In my test (see onDelete should also delete the sub-collections trails and downloads in the test file) I mock the deleteCollection function and call the firestore trigger and checks if deleteCollection is called. This is the failing response I get from the test:
Error: expect(jest.fn()).toBeCalled()
Expected number of calls: >= 1
Received number of calls: 0
It seems like jest don't match one the function I mock. What am I doing wrong?
NB! I now that this test in it self is not a good test ;)
Test file
const functions = require('firebase-functions-test');
const admin = require('firebase-admin');
const triggers = require("../../data/protocol/triggers");
const {createEvent} = require("../test_utilities");
const testEnv = functions();
const mockUpdate = jest.fn();
mockUpdate.mockReturnValue(true);
jest.mock("firebase-admin", () => ({
initializeApp: jest.fn(),
firestore: () => ({
batch: jest.fn(() => ({commit: jest.fn()})),
collection: () => (
{
doc: () => ({update: mockUpdate}),
orderBy: () => ({
limit: jest.fn(() => ({
get: jest.fn(() => ({
size: 0,
docs: {
forEach: jest.fn(),
}
}))
}))
})
}
)
}),
})
);
jest.mock('../../data/protocol/triggers', () => ({
...(jest.requireActual('../../data/protocol/triggers')),
deleteCollection: jest.fn(() => [])
})
);
describe("Protocol trigger", () => {
let adminStub, triggersStub, api;
const context = {
params: {
protocolId: 0,
}
};
beforeAll(() => {
adminStub = jest.spyOn(admin, "initializeApp");
//triggersStub = jest.spyOn(triggers, 'deleteCollection');
api = require("../../index");
});
beforeEach(() => jest.clearAllMocks());
afterAll(() => {
adminStub.mockRestore();
//triggersStub.mockRestore();
testEnv.cleanup();
});
...
it('`onDelete` should also delete the sub-collections `trails` and `downloads`', async () =>
{
const onDeleteProtocol = testEnv.wrap(api.onDeleteProtocol);
const event = {id: 0};
await onDeleteProtocol(event, {});
expect(triggers.deleteCollection).toBeCalled();
expect(onDeleteProtocol(event, {})).toBe([]);
});
});
Module
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {lastModifiedNeedsUpdate} = require("../utilities/event");
function deleteCollection(db, collectionPath, batchSize) {
return deleteQueryBatch(db, db.collection(collectionPath).orderBy('__name__').limit(batchSize), batchSize, []);
}
...
const deleteProtocol = () => functions.firestore
.document('protocols/{protocolId}')
.onDelete((event, context) => {
return deleteCollection(admin.firestore(), `protocols/${event.id}/trails`, 1);
});
module.exports = {
deleteProtocol,
createProtocol,
updateProtocol,
deleteCollection,
};
-- Frode

I resolved this by moving the helper functions (deleteCollection and deleteQueryBatch) into it's own module and mock that module.
--
Frode

Related

In Jest, how do I cause a function called within the function to return a specific value

This is the function I am testing (stripped down for simplicity's sake):
populate.js->
const { createSessionID } = require('./populate-template-utilities');
const createFile = async () => {
const responseHeader = {};
responseHeader.SessionID = createSessionID();
return responseHeader;
};
module.exports = {
createFile,
};
The function this function calls:
populate-template-utilities ->
const createSessionID = () => {
const digits = (Math.floor(Math.random() * 9000000000) + 1000000000).toString();
return `PAX${digits}`;
};
module.exports = {
createSessionID,
};
And my unit test (again stripped down):
const { createSessionID } = require('../app/lib/populate-template-utilities');
describe('create XML for output files', () => {
const mockID = jest
.spyOn(createSessionID)
.mockImplementation(() => 'PAX123456');
it('should create a PAX File', async () => {
const result = await createFile();
expect(result).toEqual(getFile);
});
});
I want createSessionID to return 'PAX123456' and think mockID should do it, but it's erroring with:
Cannot spy the undefined property because it is not a function; undefined given instead
The spyOn method needs at least two parameters: object and methodName.
Try sth like this:
import * as populateTemplateUtils from "../sessionStuff";
import { createFile } from "../createFile";
describe('create XML for output files', () => {
it('should create a PAX File', async () => {
jest
.spyOn(populateTemplateUtils, 'createSessionID')
.mockReturnValue('PAX123456');
const result = await createFile();
expect(result).toEqual({"SessionID": "PAX123456"});
});
});
It all started to work when I changed the:
module.exports = {
createSessionID,
};
to:
export const createSessionID = () => {

Jest sessionStorage cannot be mocked

I have a util function that calls the sessionStorage. it fails saying received calls are 0. It fails due to sessionStorage accessing. It works if that part is commented out. I tried mocking the sessionStorage but still no luck.
Here's the test,
it("should be to search a composition query", async () => {
const compositionQuery = "1 and 2";
jest.mock("./composition.utils", () => {
const original = jest.requireActual("./composition.utils");
return {
...original,
createCompositionQuery: jest
.fn()
.mockImplementation(() => mockCompositionQuery),
};
});
Storage.prototype.getItem = jest
.fn()
.mockReturnValue(mockSearchHistory.toString());
const user = userEvent.setup();
const submit = jest.fn();
render(<Composition onSubmit={submit} />);
const compositionInput = screen.getByTestId("composition-query");
await user.click(compositionInput);
await waitFor(() => user.type(compositionInput, compositionQuery));
await waitFor(() => user.click(screen.getByTestId("search-btn")));
await waitFor(() =>
expect(submit).toBeCalledWith(
mockCompositionQuery,
`Composition of ${compositionQuery}`
)
);
});
Here's the util function that I'm mocking.
export const createCompositionQuery = (search: string) => {
const query: Query = {
type: "",
columns: addColumnsToDisplay(),
rowFilters: [],
customRowFilterCombination: "",
};
const searchTokens = search.split(/\s+/);
searchTokens.forEach((eachToken) => {
const history = JSON.parse(
window.sessionStorage.getItem("openmrsHistory")
);
const operandQuery = history[parseInt(eachToken) - 1];
const jsonRequestObject = operandQuery.parameters;
jsonRequestObject.customRowFilterCombination = formatFilterCombination(
jsonRequestObject.customRowFilterCombination,
query.rowFilters.length
});
return { query };
};
Here's the github link for the full implementation

MockReturnValue is returning undefined on axios wrapper function

I am trying to test my componentDidMount and ensure that this axiosRequest returns an array. However no matter how I mock my axiosRequest function, it always returns undefined. What am I missing??
My axiosRequest wrapper fn:
export const axiosRequest = (type, url, body, headers) => {
return axios[type](url,
{
...body,
sharedLinkToken: store.getState()?.token ? store.getState()?.token : null,
activeWorkspace: body?.activeWorkspace ? body?.activeWorkspace :
store.getState()?.auth?.org?.activeWorkspace,
},
{ ...headers },
).then((res) => res?.data);
};
Calling said function in my CDM
async componentDidMount() {
try {
const fieldTypes = await axiosRequest('post', '/api/custom-fields/types');
console.log('fieldTypes: ', fieldTypes);
this.setState({ fieldTypes });
} catch (e) {
this.setState({ disable: true });
}
}
My test with its imports with my various ways to mock this function:
import * as axiosRequest from '../../../../utilities/utilities';
let mockAxiosRequest;
// jest.mock('axios');
// jest.mock('../../../../utilities/utilities', () => ({
// axiosRequest: jest.fn(),
// }));
beforeEach(() => {
props = {};
wrapper = shallow(<CreateCustomField {...props} />);
instance = wrapper.instance();
mockAxiosRequest = jest.spyOn(axiosRequest, 'axiosRequest');
});
it('Should find the wrapper', async () => {
mockAxiosRequest.mockResolvedValue(['stuff']);
// mockAxiosRequest.mockResolvedValue(() => ['stuff']);
// mockAxiosRequest.mockResolvedValueOnce(['stuff']);
// mockAxiosRequest.mockReturnThis(['stuff']);
// mockAxiosRequest.mockImplementation(() => ['stuff']);
// mockAxiosRequest.mockImplementation(() => Promise.resolve(['stuff']));
expect(wrapper.find('.create-custom-fields-modal').length).toBe(1);
expect(instance.state.fieldTypes.length).toBe(1);
});
I found that my mocks were working fine, however the componentDidMount was the issue. So because of this, I am doing everything manually, and everything is working as expected.
beforeEach(() => {
props = {};
mockAxiosRequest = jest.spyOn(axiosRequest, 'axiosRequest');
wrapper = shallow(<CreateCustomField {...props} />, { disableLifecycleMethods: true });
instance = wrapper.instance();
instance.setState({ fieldTypeOptions: [{ persistent_id: '123',field_type: 'Date & Time' }] });
});
Then my two tests that are now passing:
it('Should find the wrapper', async () => {
expect(wrapper.find('.create-custom-fields-modal').length).toBe(1);
});
it('Should call componentDidMount', async () => {
mockAxiosRequest.mockResolvedValueOnce([
{ persistent_id: '456', field_type: 'Date & Time' },
{ persistent_id: '123', field_type: 'Number' },
]);
await instance.componentDidMount();
expect(instance.state.fieldTypeOptions.length).toBe(2);
});

Jest mockImplementationOnce is not overriding existing mock

I have a common mock module as below:
// File: /<rootDir>/utils/__mocks__/mock-foo.js
const foo = require('foo');
jest.mock('foo');
foo.myFunction = jest.fn(() => ({
value: 10
}
));
In the jest.config.js I have below line, to make above mock available in all tests.
const config = {
setupFilesAfterEnv: [
'<rootDir>/utils/__mocks__/mock-foo.js'
]
}
Below is the unit test for myFlow.js file which is using Foo.js.
File: /<rootDir>/__test__/myFlow.js
const myFlow = require('../myFlow');
const foo = require('../foo');
describe('my unit tests', () => {
beforeAll(async () => {
jest.clearAllMocks();
});
it('test my function', async () => {
// This is not working, I always get value as 10
foo.myFunction.mockImplementationOnce(() => ({
value: 20,
}));
const result = await myFlow.someFunction();
expect(result).toBeTruthy();
});
});
How can I override the existing mock with jest mockImplementationOnce?

How to test axios get request function in jest/enzyme tests?

I have follow React component which render as a child in parent component and props are passed:
<Component
localStorageValue={'test-walue'}
requestDataFunc={getData}
requestUserData={getUserData}
expectedResponseKey={'key'}
dataType={'test}
activePage={'index'}
saveData={this.setData}
/>
so requestDataFunc is a funtion which passed to component and runned in componentDidMount :
componentDidMount() {
requestDataFunc().then(({ data }) => {
const { selectedDataItems } = this.state;
const expectedResponseData = data[expectedResponseKey];
let interimDataArr = [];
expectedResponseData.forEach((item) => {
interimDataArr = [...interimDataArr, {
...item,
active: selectedDataItems.length ? selectedDataItems.some((selectedItemId) => selectedItemId === item.id) : false,
}];
});
}
but when I run my tests, I got:
TypeError: Cannot read property 'then' of undefined
requestDataFunc().then(({ data }) => {
const { selectedDataItems } = this.state;
const expectedResponseData = data[expectedResponseKey];
let interimDataArr = [];
I just starting to test render component:
describe('correct component render', () => {
const defaultProps = {
localStorageValue: 'test-walue',
requestDataFunc: jest.fn(),
requestUserData: jest.fn(),
expectedResponseKey: 'key',
dataType: 'test',
activePage: 'index',
saveData: jest.fn(),
};
const wrapper = shallow(<ComponentName { ...defaultProps } />);
test("render component", () => {
expect(wrapper.length).toEqual(1);
});
});
I suppose that I need to mock somehow request and data that this request should receive. How to do this correctly?
Have you tried mocking promise as below:
var mockPromise = new Promise((resolve, reject) => {
resolve(<mock response similar to actual promise response>);
});
describe('correct component render', () => {
const defaultProps = {
localStorageValue: 'test-walue',
requestDataFunc: jest.fn().mockReturnValueOnce(mockPromise),
requestUserData: jest.fn(),
expectedResponseKey: 'key',
dataType: 'test',
activePage: 'index',
saveData: jest.fn(),
};
const wrapper = shallow(<ComponentName { ...defaultProps } />);
test("render component", () => {
expect(wrapper.length).toEqual(1);
});
});
Axios get method returns a promise, so you when you mock that method you also need to return a Promise
jest.fn(() => Promise.resolve({}))
More on Jest async mocking
Relevant answer from SO

Categories

Resources