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

注意

我们建议将 act await 和一个 async 函数一起使用。虽然同步版本在许多情况下都能工作,但在并非所有情况下都能工作,并且由于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 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);

    // ✅ Render the component inside 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 />);
    });

    // ✅ 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 测试库 等库来减少样板代码。

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