はじめに
React Testing LibraryとJestを使ってテストコードを書いていたところ、toBeInTheDocument() に関するエラーにつまずいたので、備忘録も兼ねて記事にまとめておきます。
問題
テストを実行したところ、以下のエラーが出ました。
received value must be an HTMLElement or an SVGElement.
Received has type: object
Received has value: {}
これは、toBeInTheDocument() に渡された値が HTMLElement ではなく、空のオブジェクト {} になっているため発生するエラーです。
該当のテストコード
describe("未入力テスト", () => {
it("入力をしないで登録を押すとエラーが表示されること", async () => {
render(<App />);
//useEventをセットアップ
const user = userEvent.setup();
await waitFor(() => {
expect(screen.queryByText("ロード中")).not.toBeInTheDocument();
});
//学習内容入力
await user.clear(screen.getByTestId("studyContent"));
//学習時間入力
await user.clear(screen.getByTestId("studyTime"));
//登録ボタン押下
const registerButton = screen.getByRole("button", { name: "登録" });
await user.click(registerButton);
await waitFor(() => {
const error = screen.findByTestId("errorMessage");
expect(error).toBeInTheDocument();
});
});
});
原因
findByは非同期で要素を探すメソッドで、内部で自動的に待機処理(waitFor)を含んでいます。そのため、さらに外側で waitFor を使うと、非推奨な重ねがけになり、正しく処理できないため、エラーが発生していました。
解決方法
findByTestId を使わず、代わりに getByTestId を waitFor の中で使用することで解決しました。
修正コード
await waitFor(() => {
const error = screen.getByTestId("errorMessage");
expect(error).toBeInTheDocument();
});
findByとwaitForの基本と使い分け
findByとwaitForの違いと使い分けをまとめました。
findBy~ は非同期処理のメソッドで、要素が見つかるまで自動的に待機します。内部的には waitFor
と getBy~
を組み合わせたものです。デフォルトでは最大1000msまで待機し、その時間内に要素が見つからない場合はエラーになります。特定の1つの要素を探す
ときに使います。
waitFor はコールバック関数内のアサーションが成功するまで待機する汎用的な関数です。複数のアサーションや、より複雑な条件を確認する場合に適しています。
使い分け
-
findBy~ を使うべき場合:
- 単一の要素が非同期で表示されるのを待つ必要がある場合
- シンプルに「要素が表示されるまで待つ」だけの場合
-
waitFor を使うべき場合:
- 複数の条件や複雑なアサーションを待つ必要がある場合
- 特定の状態が発生するまで待つ必要がある場合
おわりに
どちらも非同期処理で使われるため、最初は使い分けに戸惑いましたが、今回のエラーを通してそれぞれの役割の違いを理解することができました。
参考