Typescript 和 Jest:避免模拟函数的类型错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/51495473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Typescript and Jest: Avoiding type errors on mocked functions
提问by duncanhall
When wanting to mock external modules with Jest, we can use the jest.mock()
method to auto-mock functions on a module.
当想用 Jest 模拟外部模块时,我们可以使用该jest.mock()
方法在模块上自动模拟功能。
We can then manipulate and interrogate the mocked functions on our mocked module as we wish.
然后,我们可以根据需要在模拟模块上操作和询问模拟函数。
For example, consider the following contrived example for mocking the axios module:
例如,考虑以下模拟 axios 模块的人为示例:
import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';
jest.mock('axios');
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
axios.get.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
expect(axios.get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
The above will run fine in Jest but will throw a Typescript error:
上面的代码在 Jest 中运行良好,但会抛出 Typescript 错误:
Property 'mockReturnValueOnce' does not exist on type '(url: string, config?: AxiosRequestConfig | undefined) => AxiosPromise'.
属性 'mockReturnValueOnce' 在类型 '(url: string, config?: AxiosRequestConfig | undefined) => AxiosPromise' 上不存在。
The typedef for axios.get
rightly doesn't include a mockReturnValueOnce
property. We can force Typescript to treat axios.get
as an Object literal by wrapping it as Object(axios.get)
, but:
rightly 的 typedefaxios.get
不包含mockReturnValueOnce
属性。我们可以axios.get
通过将Typescript包装为来强制 Typescript 将其视为对象文字Object(axios.get)
,但是:
What is the idiomatic way to mock functions while maintaining type safety?
在保持类型安全的同时模拟函数的惯用方法是什么?
回答by hutabalian
Add this line of code const mockedAxios = axios as jest.Mocked<typeof axios>
. And then use the mockedAxios to call the mockReturnValueOnce.
With your code, should be done like this:
添加这行代码const mockedAxios = axios as jest.Mocked<typeof axios>
。然后使用 mockedAxios 调用 mockReturnValueOnce。使用您的代码,应该这样做:
import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
mockedAxios.get.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
expect(mockedAxios.get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
回答by Ankeet Maini
Please use mocked
function from ts-jest
请使用mocked
功能来自ts-jest
import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';
jest.mock('axios');
// OPTION - 1
const mockedAxios = mocked(axios, true)
// your original `it` block
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
mockedAxios.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
expect(mockedAxios.get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
// OPTION - 2
// wrap axios in mocked at the place you use
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
mocked(axios).get.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
// notice how axios is wrapped in `mocked` call
expect(mocked(axios).get).toHaveBeenCalled();
expect(result).toBe(expectedResult);
});
I can't emphasise how great mocked
is, no more type-casting ever.
我不能强调它有多棒mocked
,再也没有类型转换了。
回答by Brian Adams
To idiomatically mock the function while maintaining type safety use spyOnin combination with mockReturnValueOnce:
要在保持类型安全的同时使用spyOn和mockReturnValueOnce来惯用地模拟函数:
import myModuleThatCallsAxios from '../myModule';
import axios from 'axios';
it('Calls the GET method as expected', async () => {
const expectedResult: string = 'result';
// set up mock for axios.get
const mock = jest.spyOn(axios, 'get');
mock.mockReturnValueOnce({ data: expectedResult });
const result = await myModuleThatCallsAxios.makeGetRequest();
expect(mock).toHaveBeenCalled();
expect(result).toBe(expectedResult);
// restore axios.get
mock.mockRestore();
});
回答by Estus Flask
A usual approach to provide new functionality to imports to extend original module like declare module "axios" { ... }
. It's not the best choice here because this should be done for entire module, while mocks may be available in one test and be unavailable in another.
为导入提供新功能以扩展原始模块(如declare module "axios" { ... }
. 这在这里不是最佳选择,因为这应该针对整个模块完成,而模拟可能在一个测试中可用而在另一个测试中不可用。
In this case a type-safe approach is to assert types where needed:
在这种情况下,类型安全的方法是在需要的地方断言类型:
(axios.get as jest.Mock).mockReturnValueOnce({ data: expectedResult });
...
expect(axios.get as jest.Mock).toHaveBeenCalled();