1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Jest】findBy~メソッドを使っているのに Unable to find an element by: xxxエラーが発生

Posted at

はじめに

ローディング終了後のReactコンポーネント画面で特定の要素が存在するかテストするためfindByTestId()を使ったところUnable to find an element by xxxエラーが発生しました。
findBy~系のメソッドの仕様を把握できておらず解決に少し時間がかかったため、記事にしました。

問題

テスト対象は、マウント後に3秒ローディング画面を表示したあと、タイトルを表示するコンポーネントです。

App.jsx(テスト対象)
import "./App.css";
import { useState, useEffect } from "react";

function App() {
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    (async () => {
      // 3秒待機
      await new Promise((resolve) => setTimeout(resolve, 3000));
      setIsLoading(false);
    })();
  }, []);

  // ローディング制御
  if (isLoading) return <div>Loading</div>;

  return <div data-testid="title">Hello World!</div>;
}

export default App;

上記コンポーネントのテスト用に作成したテストファイルは次の通りです。

sample.spec.js
import "@testing-library/jest-dom";
import { screen, render } from "@testing-library/react";
import App from "../App";

describe("JISOU課題テスト", () => {
  it("タイトル表示チェック", async () => {
    render(<App />);
    const title = await screen.findByTestId("title");
    expect(title).toBeInTheDocument();
  });
});

上記テストファイルでテストすると、以下エラー(対象の要素が見つらない)が発生します。

Unable to find an element by: [data-testid="title"]

解決方法

findByTestId()のタイムアウト時間を変更すれば解決します。

sample.spec.js
import "@testing-library/jest-dom";
import { screen, render } from "@testing-library/react";
import App from "../App";

describe("JISOU課題テスト", () => {
  it("タイトル表示チェック", async () => {
    render(<App />);
    const title = await screen.findByTestId("title", 
+        undefined,   // このundefinedも必要です!理由は後述します
+        {
+            timeout:5000
+        });
    expect(title).toBeInTheDocument();
  });
});

今回の原因

findBy~系のメソッドは要素が見つかるまで待機をしてくれますが、デフォルトでは1秒しか待機してくれません。テスト対象のローディングは3秒かかるので、ローディングが終わる前にfindByTestId()がタイムアウトしてしまったのが原因です。
そのため、findByTestId()第3引数に、待機時間を5秒にするオプションを追加しました。

第2引数のundefinedの意味

第2引数にundefinedを指定した理由は、待機時間以外のオプションはデフォルトのままにしたかったからです。

わかりにくいと思うのでもう少し補足します。
findByTestId()の型情報は次の通りです。

findByTestId<HTMLElement>(
    id: Matcher,                                       // 第1引数
    options?: MatcherOptions | undefined,              // 第2引数
    waitForElementOptions?: waitForOptions | undefined // 第3引数
): Promise<HTMLElement> (+1 overload)

つまりfindBy~メソッドで変更できるオプションはMatcherOptions(第2引数)waitForOptions(第3引数)の2種類あるということです。

待機時間などのオプションはwaitForOptions(第3引数)が保持しています。

node_modules@testing-library\dom\types\wait-for.d.ts
export interface waitForOptions {
  container?: HTMLElement
  timeout?: number  // ←これが待機時間です!
  interval?: number
  onTimeout?: (error: Error) => Error
  mutationObserverOptions?: MutationObserverInit
} 

今回MatcherOptions(第2引数)は変更せず、waitForOptions(第3引数)だけを変更するため、findByTestId()の第2引数にundefindを記述していました。

おわりに

findBy~メソッドは要素取得を待機してくれる、ということだけ理解していたので、findBy~メソッド自体の待機時間が1秒だということに気付かず、原因特定に時間がかかりました...!

参考

JISOUのメンバー募集中!

プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?