最近、Zenn に全然(?)記事書けてないなぁっていうのと、フロントエンドのテスト大事やなぁと感じることが多かったので、React + Testing Library + Jest の覚書を雑に書くことにした
(特定の用途で覚書まとめたら、この内容だったら Zenn にも出せるやんか、とかそんなことがあったわけでは断じてない)
src/setupTests.js
に追加するjest.setup.js
みたいなセットアップファイルを作成し、jest.config.js
などの config ファイルのsetupFilesAfterEnv
にセットアップファイルを指定するこの transform を利用して実装されている JSX や TypeScript をトランスパイルしている
Jest の transformer には ts-jest や babel-jest、@swc/jest、esbuild-jest などがある
Jest のtest
には便利なメソッドがある
test.each([{ title:"nus3", value:"3"},{ title:"nus4", value:"4"},{ title:"nus5", value:"5"},])("$title の場合",// ${name}でtest.eachで渡したオブジェクトのプロパティの値をここでも使える({ test, value})=>{// test.eachオブジェクトの配列分ののテストが実行できる});
Testing Library は実際に対象のコンポーンエントをユーザーが操作してるようなテストを書くためのテストユーティリティ
大まかに次の流れでコンポーネントのテストは
実際に Testing Library のドキュメントに載ってるサンプルコードを少し編集して上記の流れを説明すると
import{ render, screen}from"@testing-library/react";importuserEventfrom"@testing-library/user-event";import{Login}from".";// テストしたい対象のコンポーネントtest("should show login form",()=>{render(<Login/>);// 1. 対象のコンポーネントをrenderconst input= screen.getByLabelText("Username");// 2. 対象の要素をクエリで取得 userEvent.type(input,"nus3");// 3. 対象の要素にユーザー操作を行うexpect(input).toHaveValue("nus3");// 4. 対象の要素が期待した状態になっているか確認});
詳細は次のリンクの Summary Table を見ると何が違うのかわかる
https://testing-library.com/docs/queries/about/#types-of-queries
ざっくりまとめると
Testing Library の基本原則では、テストコードはなるべくユーザーが操作するような実装を推奨しており、その基本原則に則ったクエリの優先度がある
https://testing-library.com/docs/queries/about/
@testing-library/jest-dom を使用すると、テスト時のコンポーネント状態を確認するための便利なメソッド(カスタムマッチャー)が使えるようになる
どのようなカスタムマッチャーがあるかは次の README を読むとイメージできる
https://github.com/testing-library/jest-dom
たとえば
toBeInTheDocument()
toBeVisible()
toHaveAttribute(attr: string, value?: any)
toHaveFocus()
などがある。
対象の要素の状態をテストしたい時に、一度この jest-dom にどんなカスタムマッチャーがあるのか確認しても良いかもしれない
テスト対象のコンポーネントの表示やユーザー操作の中で非同期の実装がある場合に、await + waitFor を使うことで非同期処理を待ってくれる(waitFor に渡した callback 関数がエラーを投げなくなるまで待つようになる)
実際に waitFor の実装例を Testing Library のドキュメントのサンプルコードを参考に作ってみると次のようになる
import{ render, screen}from"@testing-library/react";importuserEventfrom"@testing-library/user-event";import{Login}from".";import*as loginApifrom"./api";test("should submit login form",async()=>{const mockLoginApi= jest.fn(); jest.spyOn(loginApi,"login").mockImplementation(mockLoginApi);// submitボタンを押された際に実行されるloginのモックrender(<Login/>);const submitButton= screen.getByRole("button"); userEvent.click(submitButton);// loginApiのloginが呼ばれるまで待つawaitwaitFor(()=>expect(mockLoginAPI).toHaveBeenCalledTimes(1));// loginのAPIが呼ばれた後のコンポーネントの状態をテストできる});
logDOM という関数が@testing-library/react にはあり、テスト時に実際にクエリで取得した要素を確認することができる
import{ logDOM, render, screen}from"@testing-library/react";importuserEventfrom"@testing-library/user-event";import{Login}from".";test("should show login form",()=>{render(<Login/>);const input= screen.getByLabelText("Username");// test実行時にscreen.getByLabelText('Username')で取得したDOMが出力されるlogDOM(input);});
userEvent.keyboard()
で指定できる key 一覧バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。