测试时,引起 React 状态更新的代码应该被包装到 act(...)
When testing, code that causes React state updates should be wrapped into act(...)
我正在测试我的组件
ForecastButtons.js
export const ForecastButtons = ({ city }) => {
const [payload, setPayload] = useState(null)
const getData = () => {
fetchCityData(city).then((payload) => setPayload(payload));
}
const location = payload?.location?.name;
const currentTemp = payload?.current?.temp_c;
return(
<div className="sm:col-span-2">
<p className="block text-sm font-medium text-gray-700">Select forecast</p>
<button onClick={getData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
Today
</button>
<p key={city?.location?.id} className='my-5'>
{ location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
</p>
</div>
)
}
这是我测试的部分:
test('render weather into component', async () => {
const { getByText } = render(<ForecastButtons weather={weatherResponce} city={'London'} />);
const button = getByText('Today')
await act(async () => {
await fireEvent.click(button)
})
expect(getByText('London')).toBeInTheDocument();
})
请注意,这不是整个测试,只是部分不起作用。错误如上。如您所见,我向其中添加了 act()
,但它仍然不断抛出错误:When testing, code that causes React state updates should be wrapped into act(...)
我可以在 Medium 上的 React Testing Library and the “not wrapped in act” Errors 找到一些提示,那里有很多案例解释得很好。
第一次有用的学习:
React testing library already integrated act
with its APIs. So in most
cases, we do not need to wrap render
and fireEvent
in act
. For
example:
// With react-testing-library
it("should render and update a counter", () => {
// Render a component
const { getByText } = render(<Counter />;
...
// Fire event to trigger component update
fireEvent.click(getByText("Save"));
...
});
在我的例子中,我得到了错误(我作为初学者的假设),因为 fireEvent.click
触发 fetchData
被调用,这是一个异步调用。当它的响应返回时,fetchCityData
/getData
将被调用,但此时,更新将发生在 React 的调用堆栈之外。
解决方案
在断言之前,使用 waitFor 等待组件更新完全完成。 waitFor
是 React Testing Library 提供的一个 API 来等待包装断言在一定的超时时间内通过 window.
我修改了一些测试代码如下:
test('renders responce into paragraph', async () => {
render(<ForecastButtons weatherResponce={weatherResponce} city='London' />);
const button = screen.getByRole('button');
const label = screen.getByText('Please search for city to see current weather');
fireEvent.click(button)
await waitFor(() => {
expect(label.textContent).toBe(`Current weather in ${weatherResponce.location.name} is ${weatherResponce.current.temp_c} degrees`);
});
})
weatherResponce
只是对模拟 HTTP 请求的模拟响应,我正在用诺克做。
我正在测试我的组件
ForecastButtons.js
export const ForecastButtons = ({ city }) => {
const [payload, setPayload] = useState(null)
const getData = () => {
fetchCityData(city).then((payload) => setPayload(payload));
}
const location = payload?.location?.name;
const currentTemp = payload?.current?.temp_c;
return(
<div className="sm:col-span-2">
<p className="block text-sm font-medium text-gray-700">Select forecast</p>
<button onClick={getData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
Today
</button>
<p key={city?.location?.id} className='my-5'>
{ location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
</p>
</div>
)
}
这是我测试的部分:
test('render weather into component', async () => {
const { getByText } = render(<ForecastButtons weather={weatherResponce} city={'London'} />);
const button = getByText('Today')
await act(async () => {
await fireEvent.click(button)
})
expect(getByText('London')).toBeInTheDocument();
})
请注意,这不是整个测试,只是部分不起作用。错误如上。如您所见,我向其中添加了 act()
,但它仍然不断抛出错误:When testing, code that causes React state updates should be wrapped into act(...)
我可以在 Medium 上的 React Testing Library and the “not wrapped in act” Errors 找到一些提示,那里有很多案例解释得很好。
第一次有用的学习:
React testing library already integrated
act
with its APIs. So in most cases, we do not need to wraprender
andfireEvent
inact
. For example:// With react-testing-library it("should render and update a counter", () => { // Render a component const { getByText } = render(<Counter />; ... // Fire event to trigger component update fireEvent.click(getByText("Save")); ... });
在我的例子中,我得到了错误(我作为初学者的假设),因为 fireEvent.click
触发 fetchData
被调用,这是一个异步调用。当它的响应返回时,fetchCityData
/getData
将被调用,但此时,更新将发生在 React 的调用堆栈之外。
解决方案
在断言之前,使用 waitFor 等待组件更新完全完成。 waitFor
是 React Testing Library 提供的一个 API 来等待包装断言在一定的超时时间内通过 window.
我修改了一些测试代码如下:
test('renders responce into paragraph', async () => {
render(<ForecastButtons weatherResponce={weatherResponce} city='London' />);
const button = screen.getByRole('button');
const label = screen.getByText('Please search for city to see current weather');
fireEvent.click(button)
await waitFor(() => {
expect(label.textContent).toBe(`Current weather in ${weatherResponce.location.name} is ${weatherResponce.current.temp_c} degrees`);
});
})
weatherResponce
只是对模拟 HTTP 请求的模拟响应,我正在用诺克做。