問題
React Testing Libraryを使用してテストを実装している中で、screen.getByText('XXXX');
を使用すると以下のエラーが発生する。
Unable to find an element with the text: XXX
やったこと
まずは該当のコードをwaitFor関数で囲みます。
こうすることで非同期操作が完了するまで、テキストを取ってくるのを待ってくれるようになります。
以下が修正コードです。
await waitFor(() => {
const contentElement = screen.getByText('XXXX');
const timeElement = screen.getByText('10');
expect(contentElement).toBeInTheDocument();
expect(timeElement).toBeInTheDocument();
});
まだ同じエラーが出ましたが補足分がついてきました。
Unable to find an element with the text: XXX
This could be because the text is broken up by multiple elements.
In this case, you can provide a function for your text matcher
to make your matcher more flexible.
「取得したい要素が複数の要素の可能性があるため、もう少し柔軟なマッチャーを使用してみて」と言われています。
ここでカスタムマッチャーを使ってみて独自の条件を指定することにしました。
カスタムマッチャーを使用することで、より広範囲でテキスト合致を検索をすることができます。
await waitFor(() => {
const contentElement = screen.getByText((content, element) => {
return element.textContent.includes('XXXX');
});
const timeElement = screen.getByText((content, element) => {
return element.textContent.includes('10');
});
expect(contentElement).toBeInTheDocument();
expect(timeElement).toBeInTheDocument();
});
ここで、Unable to find an element with the text: XXXのエラーが出なくなりました。
代わりに以下のエラーが発生しました。
(If this is intentional, then use the `*AllBy*` variant of the query
(like `queryAllByText`, `getAllByText`, or `findAllByText`))
中身を読むと「Allby〜」を使ってくださいと言われています。
言う通りに、getAllbyTextを使用することにしました。以下が変更後のコードです。
await waitFor(() => {
const contentElements = screen.getAllByText((content, element) => {
return element.textContent.includes("テスト学習内容");
});
const timeElements = screen.getAllByText((content, element) => {
return element.textContent.includes("10時間");
});
expect(contentElements.length).toBeGreaterThan(0);
expect(timeElements.length).toBeGreaterThan(0);
contentElements.forEach((element) => {
expect(element).toBeInTheDocument();
});
timeElements.forEach((element) => {
expect(element).toBeInTheDocument();
});
上記のコードでエラーが発生しなくなり、無事解決をすることができました。
getByTextとgetAllByTextの違い
それぞれの違いを知っていないと使う時にエラーの原因になってしまうので、要点を記載します。
getByText
使用目的:DOM内の指定されたテキストを含む要素を1つだけ取得
結果の型:単一の要素
getAllByText
使用目的:DOM内の指定されたテキストを含むすべての要素を取得
結果の型:要素の配列
上記の通り、getAllByTextは結果が配列として返ってくるので要素の取り出しにはforEachなどを使用するようにしてください。
おわりに
今回はエラーに続くエラーで解消が大変でしたが、getAllByTextについて学ぶいい機会となりました。