Skip to content
Advertisement

How to mock a constructor instantiated class instance using jest?

Given a class Person that instantiates & uses another class Logger, how can the Logger‘s method be verified to be called when Person is instantiated in the example below?

// Logger.ts
export default class Logger {
    constructor() {}
    log(m: String) {
        console.log(m);

        // Other operations that are outside testing (e.g., file write).
        throw Error('error');
    }
}

// Person.ts
import Logger from "./Logger";
export default class Person {
    constructor() {
        const logger = new Logger();
        logger.log('created');
    }
    // ...
}

// Person.test.ts
import Person from "./Person";
import Logger from "./Logger";
describe('Person', () => {
    it('calls Logger.log() on instantiation', () => {
        const mockLogger = new Logger();
        getCommitLinesMock = jest
            .spyOn(mockLogger, 'log')
            .mockImplementation(() => {});

        new Person(); // Should call Logger.log() on instantiation.

        expect(getCommitLinesMock).toBeCalled();
    });
});

One option is to pass Logger as a constructor argument like below:

class Person {
    constructor(logger: Logger) {
        logger.log('created');
    }
    // ...
}

However, is there any other way without changing the constructor to complete the test?

Answer

You can use jest.mock(moduleName, factory, options) and it will automatically mock all the exports from the given module.

So you can do jest.mock("./Logger") and the Logger constructor and all of its methods will get replaced with mock functions (that return undefined by default) and now you can spy on the behavior of the constructor and all of its methods.

import Person from "./Person";
import Logger from "./Logger";

jest.mock("./Logger");

describe("Person", () => {
  it("calls the Logger constructor on instantiation", () => {
    new Person();
    expect(Logger).toHaveBeenCalledTimes(1);
  });
});

All mock functions have a special .mock property where various data related to the mock function is available including the instances that the mock constructor function has created, when it was invoked with new.

So, all the instances created by the mock Logger are saved in Logger.mock.instances and you can use this to spy on the method calls.

import Person from "./Person";
import Logger from "./Logger";

jest.mock("./Logger");

describe("Person", () => {
  it("calls the Logger constructor and the log method on instantiation", () => {
    new Person();
    expect(Logger).toHaveBeenCalledTimes(1);
    const mockLoggerInstance = Logger.mock.instances[0];
    const mockLogMethod = mockLoggerInstance.log;
    expect(mockLogMethod).toHaveBeenCalledTimes(1);
  });
});
Advertisement