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?
Advertisement
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); }); });