测试在不同文件中具有依赖关系的模块时。将该模块分配为jest.Mock
typescript时,会出现错误,即依赖项上不存在方法mockReturnThisOnce
(或任何其他jest.Mock方法),这是因为它先前已键入。从jest.Mock中获取typescript继承类型的正确方法是什么?
这是一个简单的例子。
依赖
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')
}
我觉得这是一个非常常见的用例,不知道如何正确输入。任何帮助将非常感激!
答案 0 :(得分:62)
使用mocked
中的ts-jest
助手,就像解释的here
// foo.spec.ts
import { mocked } from 'ts-jest/utils'
import { foo } from './foo'
jest.mock('./foo')
// here the whole foo var is mocked deeply
const mockedFoo = 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(mocked(foo.name).mock.calls).toHaveLength(1)
})
如果
tslint
ts-jest
属于您的开发依赖性,将此规则添加到您的tslint.json
:"no-implicit-dependencies": [true, "dev"]
答案 1 :(得分:30)
您可以使用类型转换,并且您的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 transpiler不知道jest.mock('../dependency');
会更改dep
的类型,因此您必须使用类型转换。由于导入的dep
不是类型定义,因此必须使用typeof dep.default
来获取其类型。
当导入的元素是一个类时,则不必使用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;
如果您不想使用开玩笑的自动模拟功能,而更喜欢创建手动模拟功能
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 :(得分:15)
有两种解决方案,都在投射所需的功能
1)使用jest.MockedFunction
import * as dep from './dependency';
jest.mock('./dependency');
const mockMyFunction = dep.myFunction as jest.MockedFunction<typeof dep.myFunction>;
2)使用笑话
import * as dep from './dependency';
jest.mock('./dependency');
const mockMyFunction = dep.default as jest.Mock;
这两种解决方案之间没有区别。第二个比较短,因此我建议使用那个。
两种转换解决方案均允许在mockMyFunction
上调用任何开玩笑的模拟函数,例如mockReturnValue
或mockResolvedValue
https://jestjs.io/docs/en/mock-function-api.html
mockMyFunction.mockReturnValue('value');
mockMyFunction
可以正常用于期望
expect(mockMyFunction).toHaveBeenCalledTimes(1);
答案 3 :(得分:7)
as jest.Mock
只需将函数强制转换为jest.Mock
就可以解决问题:
(dep.default as jest.Mock).mockReturnValueOnce('return')
答案 4 :(得分:6)
我使用@ types / jest / index.d.ts中位于def类型def上方的模式(第515行):
import { Api } from "../api";
jest.mock("../api");
const myApi: jest.Mocked<Api> = new Api() as any;
myApi.myApiMethod.mockImplementation(() => "test");
答案 5 :(得分:4)
我在/**
* Wrap a function with mock definitions
*
* @example
*
* import { myFunction } from "./library";
* jest.mock("./library");
*
* const mockMyFunction = myFunction as jest.MockedFunction<typeof myFunction>;
* expect(mockMyFunction.mock.calls[0][0]).toBe(42);
*/
中找到了这个
const mockMyFunction = myFunction
注意:当您执行mockFunction.mockReturnValue('foo')
然后执行类似myFunction
之类的操作时,您也正在更改{{1}}。
来源:https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/jest/index.d.ts#L1089
答案 6 :(得分:4)
as jest.Mock
在ts-jest中模拟以default
导出的模块的最简洁的方式,我认为可以归结为将模块转换为jest.Mock
。
代码:
import myDep from '../dependency' // No `* as` here
jest.mock('../dependency')
it('does what I need', () => {
// Only diff with pure JavaScript is the presence of `as jest.Mock`
(myDep as jest.Mock).mockReturnValueOnce('return')
// Call function that calls the mocked module here
// Notice there's no reference to `.default` below
expect(myDep).toHaveBeenCalled()
})
好处:
default
属性-您可以引用实际的导出函数名称,* as
,typeof
进行复杂的转换mocked
这样的额外依赖项。答案 7 :(得分:0)
这就是我对 jest@24.8.0 和 ts-jest@24.0.2 所做的事情:
来源:
class OAuth {
static isLogIn() {
// return true/false;
}
static getOAuthService() {
// ...
}
}
测试:
import { OAuth } from '../src/to/the/OAuth'
jest.mock('../src/utils/OAuth', () => ({
OAuth: class {
public static getOAuthService() {
return {
getAuthorizationUrl() {
return '';
}
};
}
}
}));
describe('createMeeting', () => {
test('should call conferenceLoginBuild when not login', () => {
OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
return false;
});
// Other tests
});
});
这是模拟非默认类及其静态方法的方法:
jest.mock('../src/to/the/OAuth', () => ({
OAuth: class {
public static getOAuthService() {
return {
getAuthorizationUrl() {
return '';
}
};
}
}
}));
这里应该是从类的类型到jest.MockedClass
的某种类型转换或类似的东西。但是它总是以错误结尾。所以我直接使用它,就可以了。
test('Some test', () => {
OAuth.isLogIn = jest.fn().mockImplementationOnce(() => {
return false;
});
});
但是,如果它是一个函数,则可以模拟它并进行类型对话。
jest.mock('../src/to/the/Conference', () => ({
conferenceSuccessDataBuild: jest.fn(),
conferenceLoginBuild: jest.fn()
}));
const mockedConferenceLoginBuild = conferenceLoginBuild as
jest.MockedFunction<
typeof conferenceLoginBuild
>;
const mockedConferenceSuccessDataBuild = conferenceSuccessDataBuild as
jest.MockedFunction<
typeof conferenceSuccessDataBuild
>;
答案 8 :(得分:0)
最近的库通过babel插件解决了此问题:https://github.com/userlike/joke
示例:
import { mock, mockSome } from 'userlike/joke';
const dep = mock(import('./dependency'));
// You can partially mock a module too, completely typesafe!
// thisIsAMock has mock related methods
// thisIsReal does not have mock related methods
const { thisIsAMock, thisIsReal } = mockSome(import('./dependency2'), () => ({
thisIsAMock: jest.fn()
}));
it('should do what I need', () => {
dep.mockReturnValueOnce('return');
}
请注意,dep
和mockReturnValueOnce
是完全类型安全的。最重要的是,tsserver知道depencency
已导入并分配给dep
,因此tsserver支持的所有自动重构也将起作用。
注意:我维护该库。
答案 9 :(得分:0)
这很丑陋,事实上,摆脱这种丑陋的原因是我什至看了这个问题,但是要从模块模拟中获得强类型,可以执行以下操作:
const myDep = (require('./dependency') as import('./__mocks__/dependency')).default;
jest.mock('./dependency');
请确保您需要'./dependency'
而不是直接进行模拟,否则您将获得两个不同的实例化。
答案 10 :(得分:-2)
既然我们在谈论测试,那么一种快速而肮脏的方法就是告诉TypeScript忽略这一行:
//@ts-ignore
dep.default.mockReturnValueOnce('return')