我想测试一个小型的 React Web 应用程序,其中我使用全局fetch
方法。
我试图以这种方式嘲笑fetch
:
global.fetch = jest.spyOn(global, 'fetch').mockImplementation(endpoint =>
Promise.resolve({
json: () => Promise.resolve(mockResponse)
})
);
。但是模拟似乎被忽略了,而似乎使用了内置fetch
:Error: 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
,则这是对该错误的合理解释。