如何在带有 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( ... )
来确保.then
与await
一起工作。【参考方案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】:假设您想测试解决和拒绝案例,为此您首先模拟获取行为,然后使用 Jest 的 rejects 和 resolves 带有断言块的方法
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+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