act
は、アサーションを行う前に保留中の React の更新を適用するためのテストヘルパーです。
await act(async actFn)
コンポーネントをアサーションの準備をするには、レンダリングと更新を実行するコードを await act()
呼び出しでラップします。これにより、テストがブラウザでの React の動作に近い形で実行されます。
リファレンス
await act(async actFn)
UI テストを書くとき、レンダリング、ユーザーイベント、データフェッチなどのタスクは、ユーザーインターフェースとのインタラクションの「単位」とみなすことができます。React は、これらの「単位」に関連するすべての更新が処理され、アサーションを行う前に DOM に適用されていることを確認するための act()
というヘルパーを提供しています。
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 ${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');
});
ここでは、コンテナを作成し、ドキュメントに追加し、Counter
コンポーネントをact()
内でレンダリングします。これにより、アサーションを行う前に、コンポーネントがレンダリングされ、その効果が適用されることが保証されます。
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()
内でイベントを発行します。これにより、イベントからのすべての更新がアサーションを行う前に適用されることが保証されます。
トラブルシューティング
エラーが出ています: “現在のテスト環境はactをサポートするように構成されていません(…)”
act
を使用するには、テスト環境でglobal.IS_REACT_ACT_ENVIRONMENT=true
を設定する必要があります。これは、act
が正しい環境でのみ使用されるようにするためです。
グローバルを設定しない場合、次のようなエラーが表示されます。
修正するには、Reactテスト用のグローバルセットアップファイルにこれを追加してください。
global.IS_REACT_ACT_ENVIRONMENT=true