テキスト入力フォームの特徴は「入力値をユーザーが好きに編集できる点」である。テストケースもフォームの値を編集した後UIが変わることを確かめたい場合が多い(例:入力値に応じてバリデーションチェックを行う)。フォーム入力はDOMイベントになるためuserEvent
で再現すれば良い。
イベント名はtype
1を利用する。入力内容は二番目の引数に指定する。実際のテストケースは次のようになる。
describe("フォーム入力値を編集したときのテスト", () => { it("苗字のフォームを編集すると入力したときの値でテキストが表示される", async () => { const event = userEvent.setup(); const content = render(<StateForm />); // 苗字のフォームを編集する await event.type(content.getByRole("textbox", { name: "苗字" }), "田中"); // フォームの値が変更されたことを確認 expect(content.getByRole("textbox", { name: "苗字" })).toHaveValue("田中"); // 苗字のフォームを編集したときの値でテキストが表示される expect( content.getByText("こんにちは、田中 太郎さん!") ).toBeInTheDocument(); }); });
テキスト入力フォームに元々value
が入力されている場合、type
だけでは意図取りテストできない。次のように「初期値 + typeの入力値」がフォームに入力された状態になってしまう。type
は元々入力されていた値を削除する機能は存在しないからである。
FAIL src/component/Form/NameForm.test.tsx 初期表示に関するテスト ✓ 苗字のフォームには山田と入力されている (33 ms) ✓ 名前のフォームには太郎と入力されている (7 ms) フォーム入力値を編集したときのテスト ✕ 苗字のフォームを編集すると入力したときの値でテキストが表示される (49 ms) ● フォーム入力値を編集したときのテスト › 苗字のフォームを編集すると入力したときの値でテキストが表示される expect(element).toHaveValue(田中) Expected the element to have value: 田中 Received: 山田田中 25 | 26 | // フォームの値が変更されたことを確認 > 27 | expect(content.getByRole("textbox", { name: "苗字" })).toHaveValue("田中"); | ^ 28 | 29 | // 苗字のフォームを編集したときの値でテキストが表示される 30 | expect( at Object.toHaveValue (src/component/Form/NameForm.test.tsx:27:58) Test Suites: 1 failed, 1 total Tests: 1 failed, 2 passed, 3 total Snapshots: 0 total Time: 0.986 s, estimated 1 s Ran all test suites matching /src\/component\/Form\/NameForm.test.tsx/i.
実際のフォーム編集を思い浮かべていただきたいが、フォームの値を全く違うものに変えたい場合はBackspaceキーなどで空にしてから編集することが多い。userEvent
はユーザー操作を再現するコンセプトでできているため「フォームを空にしてから」という操作を再現してやる必要がある。
Backspaceキーの動きを再現しても良いが、デフォルト入力値の変更のたびにキーの回数を変更しなければならない。
するとテストのメンテナンスが大変なので、一括で初期化するclear
2を使うと良い。
clear
APIはvalue
の値をリセットする機能を持っている。
describe("フォーム入力値を編集したときのテスト", () => { it("苗字のフォームを編集すると入力したときの値でテキストが表示される", async () => { const event = userEvent.setup(); const content = render(<StateForm />); // フォームを初期化する await event.clear(content.getByRole("textbox", { name: "苗字" })); // 苗字のフォームを編集する await event.type(content.getByRole("textbox", { name: "苗字" }), "田中"); // フォームの値が変更されたことを確認 expect(content.getByRole("textbox", { name: "苗字" })).toHaveValue("田中"); // 苗字のフォームを編集したときの値でテキストが表示される expect( content.getByText("こんにちは、田中 太郎さん!") ).toBeInTheDocument(); }); });
このように、入力値があるときは連続して入れるのか・初期化をする方が良いのかを考えながらテストする。TODOを最初に立てておくと、何を再現するのか迷わずに済む。