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 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 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