测试反应应用程序时如何模拟抓取?



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

我试图以这种方式嘲笑fetch

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

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

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

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

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

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 测试库的文档有点令人困惑。我正在做的事情可能有什么问题?

编辑

我正在尝试测试的 React 组件称为"App",是使用 Create React App 生成的,并更改为包含对fetch的调用。我很乐意提供此组件的代码,但我认为问题出在测试中。

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

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();结束我的测试文件。

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

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

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

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

为了使模拟正常工作,不应global.fetch进行其他修改。

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

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

最新更新