2
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?

React Testing Libraryで非同期処理のエラーが出た時の解消方法

Posted at

問題

React Testing Libraryを使用してテストを実装している中で、waitFor関数内で原因不明のエラーが出ていたのですが、コンソールログを入れるとうまくいったので、理由を調べてみました。

事象:

該当のコードがこちら。

afterEach(cleanup);

test("削除ボタンを押すと学習記録が削除される", async () => {
  render(<App />);

  // 削除ボタンが表示されるまで待機
  await waitFor(() => {
    const deleteButtons = screen.getAllByTestId("delete");
    expect(deleteButtons.length).toBeGreaterThan(0);
    // console.log("Initial delete buttons count:", deleteButtons.length);
  });

  // 初期の学習記録の数を取得
  const initialRecords = screen.getAllByTestId("record");
  //   console.log("Initial records count:", initialRecords.length);

  // 削除ボタンクリック
  const deleteButton = screen.getAllByTestId("delete")[0];
  fireEvent.click(deleteButton);
  //   console.log("Clicked delete button");

  // 削除後の学習記録の数を検証
  await waitFor(async () => {
    const updatedRecords = await screen.getAllByTestId("record");
    console.log("Initial records count after click:", initialRecords.length);
    console.log("Updated records count:", updatedRecords.length);
    expect(updatedRecords.length).toBe(initialRecords.length - 1);
  });
});

上記コードの最後のwaitFor関数内にconsole.logで変数の配列要素数をとってくるようにしました。
そうするとそれまでで続けていたどこが原因かわからないエラーがなくなり、テストが通りました。

解決の理由

どうしてconsole.logを入れるとうまく行くのか謎だったのでさらに調べると、どうやらコンソールログが非同期の実行順序に影響を与えていた可能性が理由のようでした。

つまりconsole.logを挿入することで、非同期の処理が完了するまでの時間が確保されてテストが正しく実行されたということです。

別の解決策

console.logを入れる以外に方法はないか検索したところ、getAllByTestIdの代わりにfindAllByTestId を使用すると同じ結果を得ることができました。

それぞれの違いを簡単に説明すると、

getAllByTestIdは同期的に動作して、指定されたテストIDに一致する要素を即座に返します。そして見つからない場合は、エラーを投げます。

findAllByTestIdは非同期的に動作をします。指定された時間内に要素が見つかるまで待機をして要素が見つかればそれを返します。

以下がコードです。

afterEach(cleanup);

test("削除ボタンを押すと学習記録が削除される", async () => {
  render(<App />);

  // 削除ボタンが表示されるまで待機
  await waitFor(() => {
    const deleteButtons = screen.getAllByTestId("delete");
    expect(deleteButtons.length).toBeGreaterThan(0);
    // console.log("Initial delete buttons count:", deleteButtons.length);
  });

  // 初期の学習記録の数を取得
  const initialRecords = screen.getAllByTestId("record");
  //   console.log("Initial records count:", initialRecords.length);

  // 削除ボタンクリック
  const deleteButton = screen.getAllByTestId("delete")[0];
  fireEvent.click(deleteButton);
  //   console.log("Clicked delete button");

  
    // 削除後の学習記録の数を検証
  await waitFor(
    async () => {
      await screen.findAllByTestId("record");
      await new Promise((r) => setTimeout(r, 2000));
    },
    { timeout: 3000 }
  );

  const updatedRecords = await screen.findAllByTestId("record");
  console.log(updatedRecords.length);
  expect(updatedRecords.length).toBe(initialRecords.length - 1);
  
});

今回はupdateRecordsの要素を取得するまでに時間がかかっているようで、findAllByTestIdで待機時間を指定して取得する方法でうまくいきました

上記ではawait new Promise((r) => setTimeout(r, 2000));で2秒間待機をするように指定しています。

そして{ timeout: 3000 }を設定することで、3秒以内にwaitFor関数の条件が満たされなかったらエラーをスローするようにしています。

おわりに

今回は何が原因でエラーが出ているのかわからず、ひたすらトライアンドエラーを繰り返していましたが、コンソールログをさまざまなところに設定することが解決の糸口につながりました。

2
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
2
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?