【问题标题】:How to unit test API calls with mocked fetch() in react-native with Jest如何在带有 Jest 的 react-native 中使用模拟的 fetch() 对 API 调用进行单元测试
【发布时间】:2016-07-04 08:13:56
【问题描述】:

在 React Native 中,我使用 fetch 来执行网络请求,但是 fetch 不是明确需要的模块,因此在 Jest 中似乎无法模拟。

即使尝试在测试中调用使用fetch 的方法也会导致:

ReferenceError:未定义提取

有没有办法在 Jest 的原生反应中测试此类 API 请求?

【问题讨论】:

    标签: unit-testing react-native jestjs


    【解决方案1】:

    在您的测试用例中,您可以使用 Jest 的模拟来模拟您想要的任何功能:

    fetch = jest.fn(() => Promise.resolve());
    

    这种方法仅适用于基于 Promise 的测试用例(请参阅 Jest 文档中的 pit)。

    fetch 是一个异步函数而言,您需要使用pit 运行所有测试(阅读有关异步测试的更多信息here)。

    【讨论】:

    • 仅供参考,您现在可以通过返回 Promise 来使用 it
    • 然后如何使用这个 fetch 变量?如果它是全球性的,它会干扰其他测试吗?许多其他回复建议用这个修改 global.fetch 但我不想这样做
    • @ColinD 此覆盖将限制在函数/文件的范围内。如果你只想在一个测试中覆盖它,你可以考虑在it函数的范围内使用上面的sn-p。
    • 感谢您的回复。我在想,如果你在 it() 的上下文中使用它,那么它就不会真正成为它的本地函数,因为它会修改全局提取。我在想,也许正确执行此操作的方法是模拟导入,并拥有依赖 fetch 从导入中获取它的代码,例如codeundertest.js import fetch from 'myfetch' codeundertest.test.js jest.mock('./myfetch', () => Promise.resolve({ hello: 'world' });
    • 哦,你是对的。对于误导性信息,我们深表歉意。您也可以使用afterEach 来恢复模拟。例如,在describe 的主体中,您可以在const originalFetch = fetch 中运行afterEach,然后在it 中安全地覆盖fetch,如上所示。
    【解决方案2】:

    您可以使用jest-fetch-mock npm 包覆盖全局提取对象,而不是滚动您自己的模拟。该软件包允许您设置虚假响应并验证发送的请求。有关广泛的使用示例,请参阅该链接。

    【讨论】:

    • 另外,您的模拟不必是特定于 Jest 的。 fetch-mock 有一个非常好的 API,并且比 jest-fetch-mock 功能更全。
    • fetch-mock 和几乎所有其他用于模拟 fetch 的模块的问题在于它们需要太多的设置。他们应该取消 matcher 并自动拦截所有 fetch 调用。我不想在单元测试中复制我的 Express 设置,只是为了确保被测试的代码接收到合法的 Response 对象。
    【解决方案3】:

    另一种模拟全局 fetch 对象的方法:

    const mockSuccesfulResponse = (
      status = 200,
      method = RequestType.GET,
      returnBody?: object
    ) => {
      global.fetch = jest.fn().mockImplementationOnce(() => {
        return new Promise((resolve, reject) => {
          resolve({
            ok: true,
            status,
            json: () => {
              return returnBody ? returnBody : {};
            },
          });
        });
      });
    };
    

    上面的辅助方法可以任意修改 :-) 希望对某人有所帮助

    【讨论】:

    • global.fetch = jest.fn(() => Promise.resolve());这也有效!
    • 我会使用json: () => Promise.resolve({ ... }) 来确保.thenawait 一起工作。
    【解决方案4】:

    我通过添加isomorphic-fetch 解决了这个问题。

    $ npm install --save isomorphic-fetch
    

    并像使用它

    import fetch from 'isomorphic-fetch';
    ...
    fetch('http://foo.com');
    

    whatwg-fetch 也可以工作

    【讨论】:

    • 这不能回答问题。您如何在测试中模拟 fetch
    • 加载原始问题是因为用户也遇到了“ReferenceError: fetch is not defined”错误。引入 isomorphic-fetch 可能是朝着解决方案迈出的一步,这取决于使用什么风格的 mocking fetch。例如,如果使用 MSW,那么这就是获取解析并让 MSW 拦截获取请求所需的全部内容。
    【解决方案5】:

    正如@ArthurDenture 建议的那样,您可以使用fetch-mock,但您需要安装一些额外的软件包才能使其与React Native 和Jest 一起使用:

    $ npm install --save-dev fetch-mock
    $ npm install --save-dev babel-plugin-transform-runtime
    $ npm install --save-dev babel-preset-env
    

    然后,您可以在测试中模拟获取请求。这是一个例子:

    // __tests__/App.test.js
    import React from 'react';
    import App from '../App';
    import fetchMock from 'fetch-mock';
    import renderer from 'react-test-renderer';
    
    it('renders without crashing', () => {
      fetchMock.mock('*', 'Hello World!');
      const rendered = renderer.create(<App />).toJSON();
      expect(rendered).toBeTruthy();
    });
    

    【讨论】:

      【解决方案6】:

      假设您想测试解决和拒绝案例,为此您首先模拟获取行为,然后使用 Jestrejectsresolves 带有断言块的方法

      
      function fetchTodos() {
        return fetch(`${window.location.origin}/todos.json`)
          .then(response => response.json())
          .catch(error => console.log(error))
      }
      
      describe('fetchTodos', () => {
        it('returns promise resolving to parsed response', () => {
          global.fetch = jest.fn(() => Promise.resolve({ json: () => ''}))
          expect(fetchTodos()).resolves.toBe('');
        })
        it('returns promise handling the error', async () => {
          global.fetch = jest.fn(() => Promise.reject(''))
          expect(fetchTodos()).rejects.toBe('')
        })
      })
      
      

      【讨论】:

        【解决方案7】:

        如 react-testing-library 文档中所示,您可以使用 jest.spyOn() 函数,该函数仅在下次调用时模拟 fetch 函数。

        const fakeUserResponse = {token: 'fake_user_token'}
        jest.spyOn(window, 'fetch').mockImplementationOnce(() => {
          return Promise.resolve({
            json: () => Promise.resolve(fakeUserResponse),
          })
        })
        

        react-testing-library

        【讨论】:

        • OP 要求模拟 React Native,而不是 web。 RN 没有window 对象。
        【解决方案8】:

        由于 jest 使用 fetch-mock 时出现问题,我已发布 fetch-mock-jest。它基本上提供了完整的fetch-mock api,但有一些专门针对 jest 的助手,并且开箱即用,无需自己进行任何棘手的接线

        【讨论】:

          猜你喜欢
          • 2020-08-17
          • 2020-07-24
          • 2019-09-03
          • 2018-01-29
          • 1970-01-01
          • 1970-01-01
          • 2021-11-05
          • 2020-09-16
          • 1970-01-01
          相关资源
          最近更新 更多