はじめに
PromiseをawaitせずにReactコンポーネントのpropsとして引き回すように修正し、テストコードで単に値を Promise.resolve() でラップするようにしたらテストがじゃんじゃん落ちて困った。ちょっと試行錯誤して直せたので事例共有とします。たぶんworkaroundなのでタイトルもそのようなテンションにしています。
利用しているライブラリのバージョンは以下の通り。
- react, react-dom 19.2.4
- @testing-library/react 16.3.2
- jsdom 29.0.2
やりかた
use 関数*1を使ってPromiseの解決を待つ (suspendする) コンポーネントがあったとして:
import { use } from "react"; type SuspendComponentProps = { data: Promise<string>; }; export function SuspendComponent({ data }: SuspendComponentProps) { const str = use(data); return <h1>Hello, {str}!</h1>; }
コンポーネントに渡したPromiseが解決された後の状態をテストするとき、単に render 関数を呼ぶのではなく、render 関数の呼び出しを await act(async () => ...) で囲んでやる必要がある。つまり、以下のテストコードは1つ目のテストケース (rendered with await act(async () => ...)) しか通過しない。2つ目以降のテストケースでは screen.findByText("Hello, foo!") が1秒でタイムアウトする。
import { act, render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import { SuspendComponent } from "./SuspendComponent"; import { Suspense, ReactNode } from "react"; function wrapper({ children }: { children: ReactNode }) { return <Suspense fallback={<div>loading...</div>}>{children}</Suspense>; } describe("SuspendComponent", () => { it("rendered with `await act(async () => ...)`", async () => { await act(async () => { render(<SuspendComponent data={Promise.resolve("foo")} />, { wrapper }); }); expect(await screen.findByText("Hello, foo!")).toBeInTheDocument(); }); it.fails("rendered with `act(() => ...)`", async () => { act(() => { render(<SuspendComponent data={Promise.resolve("foo")} />, { wrapper }); }); expect(await screen.findByText("Hello, foo!")).toBeInTheDocument(); }); it.fails("rendered without `act(async () => ...)`", async () => { render(<SuspendComponent data={Promise.resolve("foo")} />, { wrapper }); expect(await screen.findByText("Hello, foo!")).toBeInTheDocument(); }); });
以下のリポジトリで実際に動作確認できる。
なぜこうなるのか
どうやら以下のissueの事象が起こっていそう。すごくざっくり言うと、React 18→19で挙動が変わったことの影響を受けているらしい。
12:21 追記: act や render 関数を非同期にする、というPRが用意されているっぽい。非互換変更なのでいろいろありそう。
React Testing Libraryでuse関数を使ってuse関数を使うコンポーネントをテストする (workaround編) - 私が歌川ですb.hatena.ne.jp
- [react]
へーと思って調べてみたら <a href="https://github.com/testing-library/react-testing-library/pull/1214" target="_blank" rel="noopener nofollow">https://github.com/testing-library/react-testing-library/pull/1214</a> で render 関数を非同期にして内部で await act(...) 相当のことをするようになるっぽい。これマージされると良いですね。
2026/04/09 10:31
おわりに
React Testing Libraryでuse関数を使ってsuspendするコンポーネントをテストする場合、もうしばらくは render 関数を await act(async () => ...) で囲む必要がありそう。少なくとも、自分が遭遇したパターンでは、この記事に書いたような修正が有効そう。
*1:useで始まる関数だけどフックではないので、コンポーネントやカスタムフック中の条件分岐やループの中で呼び出してもよい