在 class 'a' 中调用的来自 class 'b' 的模拟方法

Mock method from class 'b' that is called in class 'a'

我正在尝试测试 class test.controller.ts 中的一个方法,这个方法也恰好调用了另一个正在单独测试的 class 的方法,所以我想嘲笑那个电话。

这里有一个示例设置来展示我正在尝试做的事情。

TestController.test.ts

import {TestController} from "../../src/controllers/test.controller";
import {TestService} from "../../src/services/test.service";

describe("Test TestController", () => {

    test("exampleController", () => {

        jest.mock('../../src/services/test.service');
        let testService = new TestService();
        let testController = new TestController();

        // Mock testService.exampleService so it returns 2.

        let result = testController.exampleController();
        expect(result).resolves.toBe(2);
    });

});

test.controller.ts

import {TestService} from "../services/test.service";

export class TestController {
    private testService: TestService;

    constructor() {
        this.testService = new TestService();
    }

    public async exampleController() {
        return await this.testService.exampleService();
    }
}

test.service.ts

export class TestService {
    public async exampleService() {
        return 1;
    }
}

如何模拟方法 'exampleService' 以便从 test.controller.ts 调用方法 'exampleController' 时使用这个模拟版本?

对于模拟函数,您可以使用 sinon 库。可以如下图所示完成:

let testService = new TestService();
      sinon.stub(testService, "exampleService").callsFake(function fakeFn() {
                return 2;
     });

如果您需要指定 return 值并用模拟函数完全替换函数的实现,可以使用 jest.fnmockImplementationOnce 方法来完成模拟函数。

TestController.test.ts 应该看起来像

import {TestController} from "../../src/controllers/test.controller";
import {TestService} from "../../src/services/test.service";

describe("Test TestController", () => {
    test("exampleController", () => {
        jest.mock('../../src/services/test.service');
        let testService = new TestService();
        let testController = new TestController();

        // Mock testService.exampleService so it returns 2.
        testService.exampleService = jest.fn().mockImplementationOnce(() => Promise.resolve(2));

        let result = testController.exampleController();
        expect(result).resolves.toBe(2);
    });

});

您需要模拟 TestController class 的 testService 字段。 但是使用您当前的代码,这是不可能的,因为它是私有成员。
这就是首选使用依赖注入的原因,因此我们需要将您的构造函数更改为此,

  constructor(testService: TestService) {
        this.testService = testService;
    }

我们现在传递 TestService 的对象,而不是在构造函数中实例化 testService,以便于模拟。

然后你可以这样测试,

import {TestController} from "./controller";
import {TestService} from "./service";

jest.mock('./service.ts')

describe("Test TestController", () => {

    test("exampleController", async () => {
       
        let testService = new TestService();
        jest.spyOn(testService, 'exampleService').mockResolvedValue(2)

        let testController = new TestController(testService);

        let result = await testController.exampleController();
       
        expect(result).toBe(2);
    });

});

这里你创建一个TestService的对象。
然后你在 testService 对象的 exampleService 方法上创建一个间谍,并将其解析值模拟为 return 2.
然后你把它传给TestController的构造器,这个叫依赖注入,测试起来更方便。
然后你继续按照你的期望断言。