act 是一个测试助手,用于在进行断言之前应用挂起的 React 更新。

await act(async actFn)

要准备一个组件进行断言,请将渲染它并执行更新的代码包装在一个 await act() 调用中。这会使您的测试运行方式更接近 React 在浏览器中的工作方式。

注意

您可能会发现直接使用 act() 有点过于冗长。为了避免一些样板代码,您可以使用像 React Testing Library 这样的库,它的助手是用 act() 包装的。


参考

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();
});

注意

我们建议将 actawaitasync 函数一起使用。虽然同步版本在很多情况下都能正常工作,但在所有情况下都不能正常工作,并且由于 React 内部调度更新的方式,很难预测何时可以使用同步版本。

我们将在未来弃用并删除同步版本。

参数

  • 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 ${this.state.count} times`;
}, [count]);

return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>
Click me
</button>
</div>
)
}

在测试中渲染组件

要测试组件的渲染输出,请将渲染包装在 act()

import {act} from 'react';
import ReactDOM from 'react-dom/client';
import Counter from './Counter';

it('can render and update a counter', async () => {
container = document.createElement('div');
document.body.appendChild(container);

// ✅ Render the component inside act().
await act(() => {
ReactDOM.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 ReactDOM 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 />);
});

// ✅ Dispatch the event inside 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() 内部调度事件。这确保在进行断言之前应用来自事件的所有更新。

陷阱

不要忘记,只有在将 DOM 容器添加到文档中时,调度 DOM 事件才有效。您可以使用像 React Testing Library 这样的库来减少样板代码。

故障排除

我收到一个错误:“当前测试环境未配置为支持 act(…)"

使用 act 需要在您的测试环境中设置 global.IS_REACT_ACT_ENVIRONMENT=true。这是为了确保 act 仅在正确的环境中使用。

如果您没有设置全局变量,您将看到如下错误

控制台
警告:当前测试环境未配置为支持 act(…)

要解决此问题,请将其添加到您的 React 测试全局设置文件中

global.IS_REACT_ACT_ENVIRONMENT=true

注意

在像 React Testing Library 这样的测试框架中,已经为您设置了 IS_REACT_ACT_ENVIRONMENT