使用 TypeScript 在 Jest 中模拟依赖关系

当测试在另一个文件中具有依赖项的模块并将该模块指定为时,TypeScript 会给出一个错误,指出该方法(或任何其他方法)在依赖项上不存在,这是因为它以前是类型化的。jest.mockmockReturnThisOncejest.mock

让 TypeScript 从中继承类型的正确方法是什么?jest.mock

下面是一个简单示例。

屬地

const myDep = (name: string) => name;
export default myDep;

test.ts

import * as dep from '../depenendency';
jest.mock('../dependency');

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  dep.default.mockReturnValueOnce('return')
}

我觉得这是一个非常常见的用例,不知道如何正确键入它。


答案 1

您可以使用类型转换,您应该如下所示:test.ts

import * as dep from '../dependency';
jest.mock('../dependency');

const mockedDependency = <jest.Mock<typeof dep.default>>dep.default;

it('should do what I need', () => {
  //this throws ts error
  // Property mockReturnValueOnce does not exist on type (name: string)....
  mockedDependency.mockReturnValueOnce('return');
});

TS转译器不知道更改类型,因此您必须使用类型转换。由于导入不是类型定义,因此您必须使用 获取其类型。jest.mock('../dependency');depdeptypeof dep.default

以下是我在使用Jest和TS期间发现的其他一些有用的模式

当导入的元素是一个类时,您不必使用typeof,例如:

import { SomeClass } from './SomeClass';

jest.mock('./SomeClass');

const mockedClass = <jest.Mock<SomeClass>>SomeClass;

当您必须模拟某些节点本机模块时,此解决方案也很有用:

import { existsSync } from 'fs';

jest.mock('fs');

const mockedExistsSync = <jest.Mock<typeof existsSync>>existsSync;

如果您不想使用jest自动模拟,并且更喜欢创建手动模拟

import TestedClass from './TestedClass';
import TestedClassDependency from './TestedClassDependency';

const testedClassDependencyMock = jest.fn<TestedClassDependency>(() => ({
  // implementation
}));

it('Should throw an error when calling playSomethingCool', () => {
  const testedClass = new TestedClass(testedClassDependencyMock());
});

testedClassDependencyMock()创建模拟对象实例可以是类或类型或接口TestedClassDependency


答案 2

使用模拟助手,这里解释的那样

// foo.spec.ts
import { foo } from './foo'
jest.mock('./foo')

// here the whole foo var is mocked deeply
const mockedFoo = jest.mocked(foo, true)

test('deep', () => {
  // there will be no TS error here, and you'll have completion in modern IDEs
  mockedFoo.a.b.c.hello('me')
  // same here
  expect(mockedFoo.a.b.c.hello.mock.calls).toHaveLength(1)
})

test('direct', () => {
  foo.name()
  // here only foo.name is mocked (or its methods if it's an object)
  expect(jest.mocked(foo.name).mock.calls).toHaveLength(1)
})