2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Testing Library の getByLabelText は 実際にレンダリングされる HTML に完全一致じゃないといけない

2
Posted at

はじめに

Chakra UI でフォームをテストしていたとき、getByLabelText で要素が取得できずにテストが落ちるという問題に詰まりました。

ラベルのテキストは合っているはずなのに、なぜ?というやつです。

該当のコード

// CardForm.tsx
export function CardForm() {
	return (
      <form onSubmit={handleSubmit(onSubmit)} noValidate>
        <VStack spacing={4} align="stretch">
          <FormControl id="id" isInvalid={!!errors.id} isRequired>
            <FormLabel>名刺ID</FormLabel>
            <Input
              {...register("id", {
                required: true,
                pattern: /^[a-zA-Z0-9]+$/i,
              })}
              type="text"
            />
		{/* 以下省略... */}
	)
}
// CardForm.spec.tsx
describe("UserCardForm", () => {
  it("全項目を入力して登録ボタンを押すとHomeに遷移する", async () => {
	render(
      <ChakraProvider>
        <CardForm />
      </ChakraProvider>
    );
    
    const user = userEvent.setup();

    await user.type(screen.getByRole("textbox", { name: "名刺ID" }), "testUser1");
    // 以下省略...
})

何が起きていたか

Chakra UI の FormControlisRequired を設定すると、FormLabel の末尾に必須インジケーター * が自動で付加されます。

今回のコードだと、実際にレンダリングされる HTML は以下のようになります。

<label for="id">
  名刺ID
  <span aria-hidden="true" role="presentation"> *</span>
</label>

aria-hidden="true" が付いているので「スクリーンリーダーには読まれない」のですが、textContent としては含まれます

Testing Library の getByLabelText("名刺ID") はラベルの textContent 全体(「名刺ID *」)を見て完全一致(exact: true)で探すので、「名刺ID」 とは一致せずに見つけられない、という状況になっていました。

対処法

① getByRole を使う(おすすめ)

アクセシブルな名前(accessible name) でマッチングするため、aria-hidden な要素を除外して正しく要素を取得できます。

// テキスト入力・テキストエリア
screen.getByRole("textbox", { name: "名刺ID" })

// コンボボックス(react-select 系)
screen.getByRole("combobox", { name: "好きな技術" })

accessible name ベースで動くので、必須インジケーターの * を気にする必要がなくなります。個人的にはこれを第一選択にしています。

② exact: false を指定する

前方一致になるため "名刺ID *" にも "名刺ID" がマッチするようになります。

screen.getByLabelText("名刺ID", { exact: false })

シンプルでわかりやすいですが、意図しない要素にマッチしてしまう可能性がある点だけ注意です。

まとめ

方法 特徴
getByRole("textbox", { name: "..." }) accessible name ベースなので最も堅牢
getByLabelText("...", { exact: false }) シンプルだが意図しないマッチのリスクあり

Testing Libraryには「文字から存在を確認する」だけでも似たようなAPIがたくさんありますので、どれが目的に合っているのかよく確認するのが大事だなと思いました。

今回のように UI ライブラリで必須フィールドを持つフォームをテストするときは、getByRole を使うのがおすすめです。

参考リンク

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?