给定一个实例化并使用另一个 class Logger
的 class Person
,如何在下面的示例中实例化 Person
时验证 Logger
的方法是否被调用?
// 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();
});
});
一种选择是将 Logger
作为构造函数参数传递,如下所示:
class Person {
constructor(logger: Logger) {
logger.log('created');
}
// ...
}
但是,有没有其他不改变构造函数来完成测试的方法呢?
回答1
您可以使用 https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options 它会自动模拟给定模块的所有导出。
所以你可以做 jest.mock("./Logger")
和 Logger
构造函数,它的所有方法都将被模拟函数替换(默认返回 undefined
),现在你可以监视构造函数及其所有方法的行为。
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);
});
});
所有模拟函数都有一个特殊的 .mock
属性,其中可以使用与模拟函数相关的各种数据,包括模拟构造函数在使用 new
调用时创建的实例。
因此,模拟 Logger
创建的所有实例都保存在 Logger.mock.instances
中,您可以使用它来监视方法调用。
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);
});
});