はじめに
screen.getAllByRoleを使用してテーブル行を取得する際にエラーとなりました。
問題
screen.getAllByRole("row")を使用して、テーブルの行数を取得しようとしたところ表題のエラーになりました。
エラーとなった際のコード
import App from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen, within, waitFor} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
describe("RegistDelete Test", () => {
it("削除ボタンを押すと記録数が1つ減っていること", async () => {
render(<App />);
const before = screen.getAllByRole("row").length;
// テーブル最初の行の削除ボタン押下
const rows = screen.getAllByRole("row");
const firstRow = rows[0];
const deleteButton = within(firstRow).getByTestId("delete");
await userEvent.click(deleteButton);
// テーブルの行が1行削除されていること
expect(screen.getAllByRole("row").length).toBe(before - 1);
});
});
解決方法
waitForを使う
※公式ドキュメント(翻訳)より引用
任意の期間待つ必要がある場合は、waitForを使用して、期待値が通過するのを待つことができます
waitForは、タイムアウトに達するまで何度でもコールバックを実行することができる。呼び出しの回数は、timeoutオプションとintervalオプションによって制限されることに注意してください。
await waitFor内にscreen.getAllByRole("row");を定義して、テーブル行を取得できるまで待つように修正
import App from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen, within, waitFor} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
describe("RegistDelete Test", () => {
it("削除ボタンを押すと記録数が1つ減っていること", async () => {
render(<App />);
+ // テーブルの行を取得できるまで待つ
+ await waitFor(() => {
+ screen.getAllByRole("row");
+ });
const before = screen.getAllByRole("row").length;
// テーブル最初の行の削除ボタン押下
const rows = screen.getAllByRole("row");
const firstRow = rows[0];
const deleteButton = within(firstRow).getByTestId("delete");
await userEvent.click(deleteButton);
// テーブルの行が1行削除されていること
expect(screen.getAllByRole("row").length).toBe(before - 1);
});
});
実行したところ、テストが期待値通りの結果となりませんでした。
削除処理前後でconsole.logで出力すると、削除処理後も数が変わっていませんでした。
削除処理が完了する前に、
expect(screen.getAllByRole("row").length).toBe(before - 1);
を実行されていたことが原因でした。
削除されるまで、処理を待つようにwateForを追加することで解決しました。
import App from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen, within, waitFor} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
describe("RegistDelete Test", () => {
it("削除ボタンを押すと記録数が1つ減っていること", async () => {
render(<App />);
// テーブルの行を取得できるまで待つ
await waitFor(() => {
screen.getAllByRole("row");
});
const before = screen.getAllByRole("row").length;
// テーブル最初の行の削除ボタン押下
const rows = screen.getAllByRole("row");
const firstRow = rows[0];
const deleteButton = within(firstRow).getByTestId("delete");
await userEvent.click(deleteButton);
// テーブルの行が1行削除されていること
+ await waitFor(() => {
expect(screen.getAllByRole("row").length).toBe(before - 1);
+ });
});
});
findByを使う
waitForでも実現可能ですが、非同期処理を待って要素を取得する場合、findByを使うほうが簡潔に実現できました。
※公式ドキュメント(翻訳)より引用
findByメソッドは、getByクエリとwaitForを組み合わせたものです。waitForオプションを最後の引数として受け取ります(例えば、await screen.findByText('text', queryOptions, waitForOptions))。
getByクエリとwaitForを組み合わせたものです。
最初にwaitForとgetByクエリで解決したことをfindByでできる…
import App from "../App";
import React from "react";
import '@testing-library/jest-dom'
import { render, screen, within, waitFor} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
describe("RegistDelete Test", () => {
it("削除ボタンを押すと記録数が1つ減っていること", async () => {
render(<App />);
- // テーブルの行を取得できるまで待つ
- await waitFor(() => {
- screen.getAllByRole("row");
- });
- const before = screen.getAllByRole("row").length;
+ const before = (await screen.findAllByRole("row")).length;
// テーブル最初の行の削除ボタン押下
const rows = screen.getAllByRole("row");
const firstRow = rows[0];
const deleteButton = within(firstRow).getByTestId("delete");
await userEvent.click(deleteButton);
// テーブルの行が1行削除されていること
await waitFor(() => {
expect(screen.getAllByRole("row").length).toBe(before - 1);
});
});
});
おわりに
最近エラーに遭遇すると、非同期処理の理解不足によるものなんじゃ…って思うことが増えてきました。
参考