【问题标题】:Type of Axios mock using jest typescript使用 jest typescript 模拟 Axios 的类型
【发布时间】: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 一个错误,因为我在null 或undefined 对象上调用data。我花了至少 2 个小时尝试各种方法来使其正常工作,但我找不到一种方法可以模拟 axios.get 并返回一些值。

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

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

【问题讨论】:

    标签: reactjs typescript axios jestjs


    【解决方案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】:

        我使用sinon 库npm 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
            

            【讨论】:

              猜你喜欢
              • 2023-04-02
              • 2022-07-21
              • 1970-01-01
              • 2020-01-17
              • 2020-01-04
              • 2019-10-01
              • 2021-04-14
              • 2019-04-12
              相关资源
              最近更新 更多