act 是一个测试辅助工具,用于在进行断言之前应用尚未处理的 React 更新。
await act(async actFn)在为断言准备组件时,请将渲染它并执行更新的代码包裹在一个 await act() 调用中。这会使你的测试行为更接近 React 在浏览器中的工作方式。
参考
await act(async actFn)
在编写 UI 测试时,渲染、用户事件或数据获取之类的任务可以被视为与用户界面的“交互单元”。React 提供了一个名为 act() 的辅助工具,它可以确保与这些“单元”相关的所有更新都已在你进行任何断言之前被处理并应用到 DOM 上。
act 这个名字来自 Arrange-Act-Assert 模式。
it ('renders with button disabled', async () => {
await act(async () => {
root.render(<TestComponent />)
});
expect(container.querySelector('button')).toBeDisabled();
});参数
async actFn: 一个包装被测组件渲染或交互的异步函数。actFn内触发的任何更新都会被加入内部的 act 队列,然后一起刷新,以处理并应用到 DOM 的任何变更。由于它是异步的,React 还会运行任何跨越异步边界的代码,并刷新任何已调度的更新。
返回值
act 不返回任何内容。
用法
在测试组件时,你可以使用 act 来对其输出进行断言。
例如,假设我们有这个 Counter 组件,下面的用法示例展示了如何测试它:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => prev + 1);
}
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>
Click me
</button>
</div>
)
}在测试中渲染组件
要测试组件的渲染输出,请将渲染包裹在 act() 中:
import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';
it('can render and update a counter', async () => {
container = document.createElement('div');
document.body.appendChild(container);
// ✅ 在 act() 中渲染组件。
await act(() => {
ReactDOMClient.createRoot(container).render(<Counter />);
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
});这里,我们创建一个容器,将其附加到文档中,并在 act() 内渲染 Counter 组件。这可以确保组件已完成渲染,并且其副作用已在进行断言之前被应用。
使用 act 可以确保在我们进行断言之前,所有更新都已被应用。
在测试中分发事件
要测试事件,请将事件分发包裹在 act() 中:
import {act} from 'react';
import ReactDOMClient from 'react-dom/client';
import Counter from './Counter';
it.only('can render and update a counter', async () => {
const container = document.createElement('div');
document.body.appendChild(container);
await act( async () => {
ReactDOMClient.createRoot(container).render(<Counter />);
});
// ✅ 在 act() 中分发事件。
await act(async () => {
button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});这里,我们使用 act 渲染组件,然后在另一个 act() 中分发事件。这可以确保来自该事件的所有更新都已在进行断言之前被应用。
故障排查
我收到一个错误:“当前测试环境未配置为支持 act(…)”
使用 act 需要在你的测试环境中设置 global.IS_REACT_ACT_ENVIRONMENT=true。这是为了确保 act 只在正确的环境中使用。
如果你没有设置这个全局变量,你会看到如下错误:
Console
警告:当前测试环境未配置为支持 act(…)
要修复此问题,请在 React 测试的全局初始化文件中添加以下内容:
global.IS_REACT_ACT_ENVIRONMENT=true