如何在带有 Jest 的 react-native 中使用模拟的 fetch() 对 API 调用进行单元测试

Posted

技术标签:

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

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

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

ReferenceError:未定义提取

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

【问题讨论】:

【参考方案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 的助手,并且开箱即用,无需自己进行任何棘手的接线

【讨论】:

以上是关于如何在带有 Jest 的 react-native 中使用模拟的 fetch() 对 API 调用进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

Jest 遇到了带有 react-native 的意外令牌

如何使用 jest+enzyme 在 react-native 中选择一个元素(文本、视图)(主要是当文本/视图嵌套在组件的深处时)?

使用 Jest 在 React-Native 中测试组件行为(点击)

react-native, jest, ts-jest: ReferenceError: React is not defined

React Native - JEST:没有找到 haste.hasteImplModulePath 选项中的 react-native/jest/hasteImpl.js

在未弹出 Redux 的情况下测试 Jest React-Native Expo CRNA