How can the instance methods be mocked for a class that is being mocked with jest.mock
?
For example, a class Logger
is mocked:
import Person from "./Person"; import Logger from "./Logger"; jest.mock("./Logger"); describe("Person", () => { it("calls Logger.method1() on instantiation", () => { Logger.method1.mockImplementation(() => {}) // This fails as `method1` is an instance method but how can the instance method be mocked here? new Person(); expect(Logger.method1).toHaveBeenCalled(); }); });
Advertisement
Answer
Automatic Mocking
Calling jest.mock
automatically mocks all the exports from the module being mocked unless a manual mock is specified using the __mocks__
directory.
So, this line jest.mock("./Logger")
has automatically replaced the Logger
constructor and all of it’s methods with mock functions allowing us to test how these functions behave.
And the information related to the instances created by Logger
is saved in Logger.mock.instances
, so we can use this to test if the methods are being called properly.
import Person from "./Person"; import Logger from "./Logger"; jest.mock("./Logger"); describe("Person", () => { it("calls method1 on instantiation", () => { const p = new Person(); // Logger constructor should have been called expect(Logger).toHaveBeenCalled(); const mockLoggerInstance = Logger.mock.instances[0]; const mockMethod1 = mockLoggerInstance.method1; // method1 should have also been called expect(mockMethod1).toHaveBeenCalled(); }); });
Using Module Factory Parameter
You can also explicitly provide a module factory by passing in a factory function as the second argument to jest.mock
. So, now the provided module factory would be used instead of Jest’s automocking feature. Refer the docs for more information.
import Person from "./Person"; import Logger from "./Logger"; const mockMethod1 = jest.fn(); jest.mock("./Logger", () => jest.fn().mockImplementation(() => ({ method1: mockMethod1, })) ); describe("Person", () => { it("calls method1 on instantiation", () => { const p = new Person(); // Logger constructor should have been called expect(Logger).toHaveBeenCalled(); // method1 should have also been called expect(mockMethod1).toHaveBeenCalled(); }); });
Note: jest.mock()
calls are hoisted, so you cannot first define a variable and then use it inside a factory function unless the variable is prefixed with mock
. And because of this we can access mockMethod1
inside the factory.
Manual Mock
You can achieve a similar behavior to module factory function by creating a manual mock located at __mocks__/Logger.js
. And now this mock implementation can be used across test files by simply calling jest.mock
.
// __mocks__/Logger.js const mockMethod1 = jest.fn(); const mockLogger = jest.fn(() => ({ method1: mockMethod1, }));
Usage is similar to the module factory function but you now also have to import the mocked method in your test.
Note: You still need to use the original module path, don’t include __mocks__
.
import Person from "./Person"; import Logger, { mockMethod1 } from "./Logger"; jest.mock("./Logger"); describe("Person", () => { it("calls method1 on instantiation", () => { const p = new Person(); // Logger constructor should have been called expect(Logger).toHaveBeenCalled(); // method1 should have also been called expect(mockMethod1).toHaveBeenCalled(); }); });