javascript 无法对TypeScript类使用jest.mock

mnemlml8  于 2023-09-29  发布在  Java
关注(0)|答案(3)|浏览(120)

我尝试按照Jest文档中的ES6 Class Mocks页面来测试TypeScript类Consumer上的方法。这个类示例化一个Provider对象并调用它的方法,所以我想模拟Provider类。
目录结构:

.
├── __tests__
│   └── consumers
│       └── Consumer.test.ts
└── js
    ├── providers
    │   └── provider.ts
    └── consumers
        └── Consumer.ts

provider.ts

export class Provider {
    constructor() {}

    public action(params) {
        // do some stuff that we need to mock
        return something;
    }
}

Consumer.ts

import {Provider} from "../providers/provider";

export class Consumer {
    private provider: Provider;

    constructor() {
        this.provider = new Provider();
    }

    public doSomething() {
        const result = this.provider.action(params);
        // do something with 'result'
    }
}

我的第一次尝试是使用默认的“自动模拟”:
Consumer.test.ts

import {Consumer} from "../../js/consumers/Consumer";

jest.mock("../../js/providers/provider");

test("Consumer doSomething", () => {
    // a mock Provider will be instantiated in Consumer's ctor:
    const consumer = new Consumer();

    // however, Provider.action() will return undefined within doSomething()
    consumer.doSomething();
});

这证明了我可以用mock替换真实的实现,但是我需要确保Provider.action()返回一个值,所以接下来我尝试了:

// at some point we can make this return something, but first check it works
const mockAction = jest.fn();
jest.mock("../../js/providers/provider", () => {
  return jest.fn().mockImplementation(() => {
    return {action: mockAction};
  });
});

test("Consumer doSomething", () => {
    // throws TypeError: provider_1.Provider is not a constructor
    const consumer = new Consumer();

    consumer.doSomething();
});

无论我如何尝试更改模拟,我都无法找到一个解决方案,使我可以在测试中正常使用Consumer。我宁愿避免创建“手动模拟”,这样我就可以保持代码库更干净,并在测试之间改变模拟实现。

jm81lzqq

jm81lzqq1#

您不必在此处使用默认导出。当使用命名导出时,您需要创建一个与模块的“形状”匹配的mock。在你的例子中:

const mockAction = jest.fn();
jest.mock("../../js/providers/provider", () => ({
 Provider: jest.fn().mockImplementation(() => ({
    action: mockAction
  }))
));
zaqlnxep

zaqlnxep2#

我似乎已经解决了这个问题,确保要模仿的依赖是默认导出:
Provider.ts

export default class Provider {}

Consumer.tsConsumer.test.ts

import Provider from "../providers/provider";

我相信这是因为jest.mock()目标是一个模块,而provider是一个模块,其中定义了一个类Provider**。如果该模块中没有默认导出,则模拟的确切目标是不明确的。通过使类成为默认导出,Jest知道将其用作mock的目标。

4xy9mtcn

4xy9mtcn3#

根据Manual Mocks => Using With ES Module Imports上的Jest文档,Jest有时需要在导入之前完成工作。在正常情况下,它可以在导入之前“提升”mock,即使在代码中它出现在导入之后。但是,在Typescript和ECMAScript支持下,它不能这样做。所以对于动态类,你必须把你的mock放在导入之前。
但还有更古怪的事情:如果您试图模拟类静态方法,则必须将模拟对象放在导入之后。
最后得到这样的代码:

... Dynamic class mock
... import statement for the dynamic class
... Mocks for static methods

注意,我所说的导入也指的是你的消费者类中的导入。也就是说,测试文件中的消费者类必须在动态类mock之后导入,但在静态方法的任何mock之前导入。
通过将esModuleInteropTypescript编译器选项设置为true,您可以解决所有这些问题,而无需求助于放置。但是,这可能会对不兼容的代码产生各种副作用,并且可能是更困难的途径。

相关问题