javascript - 如何使用 jest 模拟通过构造函数实例化的类实例?
假设有一个类 Person 实例化并使用另一个类 Logger,如何验证在下面的例子中,当 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
你可以使用 jest.mock(moduleName, 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);
});
});
答案#2
测试一个带有方法的类的另一种方式是在模拟中使用 原型。
import Person from "./Person";
import Logger from "./Logger";
jest.mock("./Person");
describe("Person", () => {
it("mock and test Person method", async () => {
jest.spyOn(Person.prototype, "somePersonMethod").mockImplementation(() => {
return "testing Person class method";
});
//expect(...
});
});
