使用 jest typescript 模拟 Axios 的类型

Posted

技术标签:

【中文标题】使用 jest typescript 模拟 Axios 的类型【英文标题】:Type of Axios mock using jest typescript 【发布时间】:2018-12-18 21:38:41 【问题描述】:

我在一个类中有以下方法:

import axios from 'axios'

public async getData() 
   const resp = await axios.get(Endpoints.DATA.URL)
   return resp.data

然后我正在尝试设置一个 Jest 测试来执行此操作:

jest.mock('axios')

it('make api call to get data', () => 
   component.getData()
   expect(axios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
)

问题是因为我没有模拟返回值,所以它给resp.data 一个错误,因为我在nullundefined 对象上调用data。我花了至少 2 个小时尝试各种方法来使其正常工作,但我找不到一种方法可以模拟 axios.get 并返回一些值。

Jest 的文档使用 javascript,所以他们给出了这个示例 axios.get.mockResolvedValue(resp),但我不能调用 mockResolvedValue,因为 TypeScript 中的 axios.get 上不存在该方法。

另外,如果你知道除了 Jest 之外的其他优秀的 React 测试库,可以轻松地为 TypeScript 做这些事情,请随时分享。

【问题讨论】:

【参考方案1】:

在文件开头:

import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

现在你可以像往常一样使用它了:

mockedAxios.get.mockRejectedValue('Network error: Something went wrong');
mockedAxios.get.mockResolvedValue( data:  );

【讨论】:

我做了完全相同的事情,但是打字稿将此错误归咎于我的 data: null :error TS2345: Argument of type ' data: null; ' is not assignable to parameter of type '(&lt;T = any&gt;(url: string, config?: AxiosRequestConfig | undefined) =&gt; AxiosPromise&lt;T&gt;) | PromiseLike&lt;(&lt;T = any&gt;(url: string, config?: AxiosRequestConfig | undefined) =&gt; AxiosPromise&lt;T&gt;)&gt;'. 不知道为什么它不起作用... 您可以尝试输入或制作任何内容: data: null as any【参考方案2】:

如果您想将jest.mock"no-any" 一起使用,请尝试以下操作:

import axios,  AxiosStatic  from 'axios'

interface AxiosMock extends AxiosStatic 
  mockResolvedValue: Function
  mockRejectedValue: Function


jest.mock('axios')
const mockAxios = axios as AxiosMock

it('make api call to get data', () => 
   // call this first
   mockAxios.mockResolvedValue(yourValue)

   component.getData()
   expect(mockAxios.get).toHaveBeenCalledWith(Endpoints.DATA.URL)
)

【讨论】:

这有效,不需要其他库,应该接受答案 这种方法也适用于我,无需添加新库。我唯一有点卡住的是如何制作它,这样我就不必在不同的测试文件中重复相同的代码..会调查它并更新评论(希望如此)【参考方案3】:

这是我个人经常使用的。

import axios from 'axios';
jest.mock('axios')

it('...', () => 
  (axios.get as jest.Mock).mockImplementationOnce(() => Promise.resolve());
  // test here
  expect(axios.get).toHaveBeenCalled()

【讨论】:

【参考方案4】:

但我不能调用 mockResolvedValue,因为 TypeScript 中的 axios.get 上不存在该方法

你可以使用断言:

(axios.get as any).mockResolvedValue(resp)

【讨论】:

项目有一个ts-lint规则"no-any": true 在前一行添加一个 tslint 忽略 1.随着项目的增长和添加更多异步调用,这不会是理想的。 2. 我使用 TypeScript 是因为它强大的类型系统,我不想绕过它。【参考方案5】:

我一直遇到is not a function 问题。如果接受的答案对您不起作用,请尝试使用大写字母 A 导入 axios 即。 Axios.

import Axios from 'axios';
jest.mock('Axios');
const mockedAxios = Axios as jest.Mocked<typeof Axios>;

【讨论】:

【参考方案6】:

我使用sinonnpm install sinon @types/sinon --save-dev 找到了一个简洁的解决方案。

那么测试代码就变成了:

let component: Component
let axiosStub: SinonStub

beforeAll(() => 
    component = new Component()
    axiosStub = sinon.stub(axios, 'get')
)

afterAll(() => 
    axiosStub.restore()
)

it('make api call to get data', async () => 
    // set up behavior
    axiosStub.withArgs(Endpoints.DATA.URL).returns(data: [])

    // method under test
    const res = await component.getData()

    // assertions
    expect(res).toEqual([])
)

【讨论】:

【参考方案7】:

另一种选择是使用jest.spyOn:

import axios from "axios";

jest.spyOn(axios, "get").mockImplementation(() => Promise.resolve(data: []));

这还为您提供了可以测试的模拟方法的好处,例如:

import axios from "axios";

// ...

const mockedGet = jest
  .spyOn(axios, "get")
  .mockImplementation(() => Promise.resolve(data: []));

// ...

expect(mockedGet).toBeCalledWith('https://example.api?q=abc&k=123');

【讨论】:

【参考方案8】:

从 Jest 24.9.0 开始,这是正确输入 axios 和 Jest 属性的方式。

我们希望类型化的模拟是模拟对象类型包含模拟对象类型和 Jest 模拟类型的联合。据我所知,目前的答案都不能做到这一点。

jest.MockedFunction

jest.MockedClass

import axios from 'axios';
jest.mock('axios');

const mockedAxios = axios as jest.MockedFunction<typeof axios>;

mockedAxios.mockResolvedValue( status: 200, data: 'mockResponse' );

// Or:
(mockedAxios.get as jest.MockedFunction<typeof mockedAxios.get>).mockResolvedValue('mockResponse');

如您所见,您可以手动转换您需要的内容,或者您​​需要一些东西来遍历所有 axios 属性/方法来键入所有内容。

为此(深度模拟类型),您可以使用 Jest 中引入的 jest.mocked() 27.4.0

import axios from 'axios';
jest.mock('axios');

const mockedAxios = jest.mocked(axios, true); 

mockedAxios.mockImplementation() // correctly typed
mockedAxios.get.mockImplementation() // also correctly typed

【讨论】:

以上是关于使用 jest typescript 模拟 Axios 的类型的主要内容,如果未能解决你的问题,请参考以下文章

使用 Typescript 从 Jest 手动模拟中导入函数

使用正确类型使用 Jest 和 Typescript 模拟 Express 请求

使用 jest typescript 模拟 Axios 的类型

用 Jest 和 Typescript 模拟一个全局对象

使用 typescript 模拟 forwardRef 组件 jest mockImplementation

无法使用 typescript 用 jest 模拟 fs - 类型上不存在属性“mockReturnValue”