ラジオボタンやセレクトボックスのように、value
を持つHTMLタグがある。
UIコンポーネントテストを書く際、value
が意図通り設定できていることをテストしたい場面がある。
ハマってしまいやすいのでメモする。
ラジオボタンの値を取得したい場合
こういう実装を書いて
<label> <input type="radio" name="extension" value="previous" /> 前泊のみ </label>
こういうテストを書くと落ちる。
it("前泊のみのラベルテキストとvalueがデグレードしていない", () => { const content = render(<StayExtensionOptionsRadio />); const previous = content.getByText("前泊のみ"); expect(previous).toBeTruthy(); expect(previous).toHaveAttribute("value", "previous"); // ここで落ちる });
FAIL src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx 初期状態のテスト ✓ ラジオボタンは全部で4つ存在する (58 ms) ✕ 前泊のみのラベルテキストとvalueがデグレードしていない (9 ms) ● 初期状態のテスト › 前泊のみのラベルテキストとvalueがデグレードしていない expect(element).toHaveAttribute("value", "previous") // element.getAttribute("value") === "previous" Expected the element to have attribute: value="previous" Received: null 14 | const previous = content.getByText("前泊のみ"); 15 | expect(previous).toBeTruthy(); > 16 | expect(previous).toHaveAttribute("value", "previous"); | ^ 17 | }); 18 | }); 19 | at Object.toHaveAttribute (src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx:16:22) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 0.859 s, estimated 1 s Ran all test suites matching /src\/component\/StayExtensionOptionsRadio\/StayExtensionOptionsRadio.test.tsx/i.
Recievedがnullになっている。
expectの第一引数は対象の要素である。previousは何か?<lavel>
である。labelのAttribute(属性)にvalueはあるか?
<label>←ここ <input type="radio" name="extension" value="previous" /> 前泊のみ ←ここ </label>←ここ
…ない。ないんだからnullで帰ってくる。
どこにvalue
はあるか?<input>
である。ということは<input>
を取得する必要がある。
ラジオボタンはWAI-ARIA1という規格でrole="radio"
2 として定義されている。そのためgetByRole()
3で要素を取得できる。
ラジオボタンを明示して要素を取得すればvalueを検証できるはず。という予想を立て、次のように修正した。 日和らず回してみるのが重要。
it("前泊のみのラベルテキストとvalueがデグレードしていない", () => { const content = render(<StayExtensionOptionsRadio />); // ラベルのテキストを取得 expect(content.getByText("前泊のみ")).toBeTruthy(); // getByRoleでrole=radioを取得し、前泊のみの選択肢を取得している(つもり) expect(content.getByRole("radio", { name: "previous" })).toHaveAttribute("value", "previous"); });
今度は次のようにエラーが出てきた。
➜ react-sandbox git:(65-radio-button) ✗ npm run test src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx > reactjs-sandbox@1.0.0 test > node scripts/test.js --watchAll=false src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx FAIL src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx 初期状態のテスト ✓ ラジオボタンは全部で4つ存在する (38 ms) ✕ 前泊のみのラベルテキストとvalueがデグレードしていない (38 ms) ● 初期状態のテスト › 前泊のみのラベルテキストとvalueがデグレードしていない TestingLibraryElementError: Unable to find an accessible element with the role "radio" and name "previous" Here are the accessible roles: radio: Name "前泊のみ": <input name="extension" type="radio" value="previous" /> Name "後泊のみ": <input name="extension" type="radio" value="next" /> Name "前泊と後泊": <input name="extension" type="radio" value="both" /> Name "宿泊なし": <input name="extension" type="radio" value="none" /> -------------------------------------------------- Ignored nodes: comments, script, style <body> <div> <div> <label> <input name="extension" type="radio" value="previous" /> 前泊のみ </label> <label> <input name="extension" type="radio" value="next" /> 後泊のみ </label> <label> <input name="extension" type="radio" value="both" /> 前泊と後泊 </label> <label> <input name="extension" type="radio" value="none" /> 宿泊なし </label> </div> </div> </body> 13 | const content = render(<StayExtensionOptionsRadio />); 14 | expect(content.getByText("前泊のみ")).toBeTruthy(); > 15 | expect(content.getByRole("radio", { name: "previous" })).toHaveAttribute("value", "previous"); | ^ 16 | }); 17 | }); 18 | at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19) at node_modules/@testing-library/dom/dist/query-helpers.js:76:38 at node_modules/@testing-library/dom/dist/query-helpers.js:52:17 at node_modules/@testing-library/dom/dist/query-helpers.js:95:19 at Object.getByRole (src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx:15:20) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 0.612 s, estimated 1 s Ran all test suites matching /src\/component\/StayExtensionOptionsRadio\/StayExtensionOptionsRadio.test.tsx/i.
「TestingLibraryElementError: Unable to find an accessible element with the role "radio" and name "previous"」に注目する。Unable
はできなかった、ということ。to find
と続くため、「見つけられなかった」となる。つまり指定のRoleは見つからなかった。という意味である。
getByRole
のクエリに問題がある、とわかったため修正方法を検討する。次に注目すべきは「Here are the accessible roles:」の部分である。実際どのようにレンダリングされているかを教えてくれている。Name
の部分に注目する。
Here are the accessible roles: radio: Name "前泊のみ": // ←Nameが{name: "xxx"}のnameに一致する <input name="extension" type="radio" value="previous" />
input
にもname
があるため混乱しやすい。roleのNameとinputのnameは別物である。<input>
のname
4はフォームコントロール要素として使われるため、WAI-ARIA Roleとは役割が異なる。
Nameの部分がWAI-ARIA Roleのname
となるため、次のように修正する。
it("前泊のみのラベルテキストとvalueがデグレードしていない", () => { const content = render(<StayExtensionOptionsRadio />); expect(content.getByText("前泊のみ")).toBeTruthy(); // 出力結果Name "前泊のみ"を参考にクエリを修正 expect(content.getByRole("radio", { name: "前泊のみ" })).toHaveAttribute("value", "previous"); });
テストもオールグリーンになった。
➜ react-sandbox git:(65-radio-button) ✗ npm run test src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx > reactjs-sandbox@1.0.0 test > node scripts/test.js --watchAll=false src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx PASS src/component/StayExtensionOptionsRadio/StayExtensionOptionsRadio.test.tsx 初期状態のテスト ✓ ラジオボタンは全部で4つ存在する (37 ms) ✓ 前泊のみのラベルテキストとvalueがデグレードしていない (13 ms) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 0.91 s, estimated 1 s Ran all test suites matching /src\/component\/StayExtensionOptionsRadio\/StayExtensionOptionsRadio.test.tsx/i.