【testing-library】TestingLibraryElementError: Unable to find an accessible element with the role 

# はじめに
screen.getAllByRoleを使用してテーブル行を取得する際にエラーとなりました。

# 問題
screen.getAllByRole("row")を使用して、テーブルの行数を取得しようとしたところ表題のエラーになりました。

エラーとなった際のコード

```javascript:delete.spec.js
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");を定義して、テーブル行を取得できるまで待つように修正

```diff_javascript:delete.spec.js
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で出力すると、削除処理後も数が変わっていませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3993509/692c5da4-b13e-4ec0-a318-63c70f65da41.png)

削除処理が完了する前に、
expect(screen.getAllByRole("row").length).toBe(before - 1);
を実行されていたことが原因でした。

削除されるまで、処理を待つようにwateForを追加することで解決しました。

```diff_javascript:delete.spec.js
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でできる…

```diff_javascript:delete.spec.js
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);
		});
	});
});
```

# おわりに
最近エラーに遭遇すると、非同期処理の理解不足によるものなんじゃ…って思うことが増えてきました。

# 参考

https://qiita.com/ham0215/items/3cbd2148d5eedc649f47#waitfor

https://testing-library.com/docs/dom-testing-library/api-async/#waitfor

https://zenn.dev/spacemarket/articles/6b52d53696ef13#%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86%E3%82%92%E5%BE%85%E3%81%A3%E3%81%A6%E3%81%8B%E3%82%89%E3%82%A2%E3%82%B5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A1%8C%E3%81%86---waitfor