如何使用笑话模拟构造函数实例化类实例?

sur*_*s02 2 javascript class mocking typescript jestjs

给定一个Person实例化并使用另一个类的类Logger,如何验证在下面的示例中实例化Logger时调用 的方法?Person

// 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();
    });
});
Run Code Online (Sandbox Code Playgroud)

一种选择是作为构造函数参数传递Logger,如下所示:

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

Run Code Online (Sandbox Code Playgroud)

但是,有没有其他方法可以在不改变构造函数的情况下完成测试呢?

SSM*_*SSM 7

您可以使用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);
  });
});
Run Code Online (Sandbox Code Playgroud)

所有模拟函数都有一个特殊的.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);
  });
});
Run Code Online (Sandbox Code Playgroud)