Skip to content
Advertisement

How to mock instance methods of a class mocked with jest.mock?

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();
  });
});
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement