测试 React 应用程序时如何模拟获取? [英] How to mock fetch when testing a React app?

查看:28
本文介绍了测试 React 应用程序时如何模拟获取?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想测试一个使用全局 fetch 方法的小型 React Web 应用程序.

I would like to test a small React web app where I use the global fetch method.

我试图以这种方式模拟 fetch:

I tried to mock fetch in this way:

global.fetch = jest.spyOn(global, 'fetch').mockImplementation(endpoint =>
  Promise.resolve({
    json: () => Promise.resolve(mockResponse)
  })
);

... 但模拟似乎被忽略,而内置的 fetch 似乎被使用:Error: connect ECONNREFUSED 127.0.0.1:80 ... 看起来像是对内置 fetch 的调用失败.

... but the mock seems to be ignored, while the built-in fetch seems to be used: Error: connect ECONNREFUSED 127.0.0.1:80 ... looks like a failed call to the built-in fetch.

然后我尝试使用 jest.fn 而不是 jest.spyOn:

I then tried to use jest.fn instead of jest.spyOn:

global.fetch = jest.fn(endpoint =>
  Promise.resolve({
    json: () => Promise.resolve(mockResponse)
  })
);

... 并惊讶地看到一个不同的错误.现在模拟似乎被考虑在内,但同时无法正常工作:

... and was surprised to see a different error. Now the mock seems to be taken into consideration, but at the same time is not working correctly:

    TypeError: Cannot read property 'then' of undefined

       8 |     this.updateTypes = this.props.updateTypes;
       9 |     this.updateTimeline = this.props.updateTimeline;
    > 10 |     fetch('/timeline/tags')
         |     ^
      11 |       .then(res => res.json())
      12 |       .then(tags => tags.map(tag => <option value={tag} key={tag} />))
      13 |       .then(options => options.sort((a, b) => a.key.localeCompare(b.key)))

老实说,我发现 Jest 和 React 测试库的文档有点混乱.我正在做的事情可能有什么问题?

I find the documentation of Jest and React Testing Library a bit confusing, honestly. What might be the problem with what I am doing?

我尝试测试的 React 组件称为App",是通过 Create React App 生成的,并更改为包括对 fetch 的调用.我很乐意提供这个组件的代码,但我相信问题出在测试上.

The React component I am trying to test is called "App", was generated with Create React App, and was changed to include a call to fetch. I can gladly provide the code for this component, but I believe that the problem lies in the tests.

在我的 App.test.js 文件的开头,我 import React from 'react';,然后 import { render, fireEvent, waitFor,screen } from '@testing-library/react';,最后import App from './App';.我随后尝试以我描述的方式之一模拟 fetch,然后声明以下测试:

At the beginning of my App.test.js file, I import React from 'react';, then import { render, fireEvent, waitFor, screen } from '@testing-library/react';, and finally import App from './App';. I subsequently attempt to mock fetch in one of the ways I described, and then declare the following test:

test('renders a list of items, upon request', async () => {
  const app = render(<App />);

  fireEvent.click(screen.getByText('Update'));

  await waitFor(() => screen.getByRole('list'));

  expect(screen.getByRole('list')).toBeInTheDocument();
  expect(screen.getByRole('list')).toHaveClass('Timeline');
});

最后,我用 global.fetch.mockRestore(); 结束我的测试文件.

Finally, I end my test file with global.fetch.mockRestore();.

推荐答案

存在 ECONNREFUSED 错误而不是 fetch is not defined 意味着 fetch> 已被填充.它不是 JSDOM 的一部分,也不是 Jest 本身的 polyfill,而是特定于当前设置.在这种情况下,polyfill 由 create-react-app 提供.

That there's ECONNREFUSED error instead of fetch is not defined means that fetch has been polyfilled. It's not a part of JSDOM and isn't polyfilled by Jest itself but is specific to current setup. In this case the polyfill is provided by create-react-app.

最好使用 jest.spyOn 模拟现有的全局函数,而不是将它们分配为 global 属性,这允许 Jest 进行清理.永远不要做像 global.fetch = jest.spyOn(global, 'fetch') 这样的事情,因为这会阻止 fetch 被恢复.这可以解释 TypeError: Cannot read property 'then' of undefined 看似正确模拟函数的错误.

It's always preferable to mock existing global function with jest.spyOn and not by assigning them as global properties, this allows Jest to do a cleanup. A thing like global.fetch = jest.spyOn(global, 'fetch') should never be done because this prevents fetch from being restored. This can explain TypeError: Cannot read property 'then' of undefined error for seemingly correctly mocked function.

模拟全局变量的正确且安全的方法是在每次测试之前模拟它们并在每次测试之后恢复:

A correct and safe way to mock globals is to mock them before each test and restore after each test:

beforeEach(() => {
  jest.spyOn(global, 'fetch').mockResolvedValue({
    json: jest.fn().mockResolvedValue(mockResponse)
  })
});

afterEach(() => {
  jest.restoreAllMocks();
});

不应该对 global.fetch 进行其他修改,以使模拟正常工作.

There should be no other modifications to global.fetch in order for a mock to work correctly.

恢复模拟和间谍的更好方法是使用 configuration 选项而不是 jest.restoreAllMocks 因为不这样做可能会导致意外的测试交叉污染,这是不可取的.

A preferable way to restore mocks and spies is to use configuration option instead of jest.restoreAllMocks because not doing this may result in accidental test cross-contamination which is never desirable.

TypeError: Cannot read property 'then' of undefined 错误出现的另一个原因是 Jest 错误地指向了 fetch 行,而该错误实际上是指另一行.如果源映射不能正常工作,就会发生这种情况.如果 fetch 被正确模拟并且在同一组件中还有其他 then,那么这是对错误的合理解释.

Another reason for TypeError: Cannot read property 'then' of undefined error to appear is that Jest incorrectly points at fetch line, and the error actually refers to another line. This can happen if source maps don't work correctly. If fetch is mocked correctly and there are other then in the same component, it's a plausible explanation for the error.

这篇关于测试 React 应用程序时如何模拟获取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆