私が歌川です

@utgwkk が書いている

React Testing Libraryでuse関数を使うコンポーネントをテストする (workaround編)

はじめに

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

以下のリポジトリで実際に動作確認できる。

github.com

なぜこうなるのか

どうやら以下のissueの事象が起こっていそう。すごくざっくり言うと、React 18→19で挙動が変わったことの影響を受けているらしい。

github.com

12:21 追記: actrender 関数を非同期にする、というPRが用意されているっぽい。非互換変更なのでいろいろありそう。

github.com

React Testing Libraryでuse関数を使ってuse関数を使うコンポーネントをテストする (workaround編) - 私が歌川です

へーと思って調べてみたら <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
b.hatena.ne.jp

おわりに

React Testing Libraryでuse関数を使ってsuspendするコンポーネントをテストする場合、もうしばらくは render 関数を await act(async () => ...) で囲む必要がありそう。少なくとも、自分が遭遇したパターンでは、この記事に書いたような修正が有効そう。

*1:useで始まる関数だけどフックではないので、コンポーネントやカスタムフック中の条件分岐やループの中で呼び出してもよい

時間のないサイト運営者リング