Lets say I have the following named export in a file customer.ts
export const saveDetails = ()=>{}
export const loadDetails = ()=>{}
assuming I'm using this in another file
import {saveDetails, loadDetails} from './customer.ts'
I want to mock mock './customer.ts' with a custom implementation. To that end I use the following code
const mockSaveDetails = jest.fn().mockImplementation(() => {});
jest.mock('./customer.ts', () => {
return {
saveDetails: mockSaveDetails
};
});
Now when I run this code I get the following error
ReferenceError: Cannot access 'mockSaveDetails' before initialization
As per the documentation on https://jestjs.io/docs/en/es6-class-mocks I understand the mock is hoisted to the top and the exception is if the variable has the prefix mock
. So as per the documentation this should work right ? If not what is the alternative to providing a mock mock implementation and spying on those implementation (like see howmany calls were made to saveDetails
for example) with certain arguments.
To mock an exported const in Jest, we can use the jest. mock method. For instance, we write: const mockTrue = { ENABLED: true }; const mockFalse = { ENABLED: false }; describe('allowThrough', () => { beforeEach(() => { jest.
To mock an imported function with Jest we use the jest. mock() function. jest. mock() is called with one required argument - the import path of the module we're mocking.
Jest has a spyOn function, which can resolve the problem and make the test much better. jest. spyOn allows a method in an object to be mocked.
Here is the solution:
index.ts
:
import { saveDetails, loadDetails } from "./customer";
export function main() {
saveDetails();
loadDetails();
}
customer.ts
:
export const saveDetails = () => {
console.log("real save details");
};
export const loadDetails = () => {
console.log("real load details");
};
index.spec.ts
:
import { main } from "./";
import { saveDetails, loadDetails } from "./customer";
jest.mock("./customer.ts", () => {
return {
saveDetails: jest.fn(),
loadDetails: jest.fn()
};
});
describe("main", () => {
it("should mock correctly", () => {
main();
expect(saveDetails).toBeCalledTimes(1);
expect(loadDetails).toBeCalledTimes(1);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59024742/index.spec.ts
main
✓ should mock correctly (5ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.626s, estimated 9s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59024742
ReferenceError: Cannot access 'mockSaveDetails' before initialization
The mock-setup must be the first thing in the file, even before the import
s. Then the mockSaveDetails
will be recognised.
It has to do with hoisting the mocks, as stated by the docs, but somehow for TypeScript this isn't done as documented, so we need to help it a little bit...
This answer doesn't work if you want to provide an implementation for one of the mocked functions.
The only solution I've found is to change the import statement, and then use spys on this.
customer.ts
const saveDetails = () => {
console.log("real save details");
};
const loadDetails = () => {
console.log("real load details");
};
export { saveDetails, loadDetails }
index.ts
import { saveDetails, loadDetails } from "./customer";
export function main() {
saveDetails();
loadDetails();
}
index.spec.ts
import { main } from ".";
import * as customer from "./customer";
describe("main", () => {
it("should mock correctly", () => {
const detailsSpy = jest.spyOn(customer, 'saveDetails')
.mockImplementation(() => {return {}});
const loadSpy = jest.spyOn(customer, 'loadDetails')
.mockImplementation(() => {return {}});
main();
expect(detailsSpy).toBeCalledTimes(1);
expect(loadSpy).toBeCalledTimes(1);
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With