マッチャーを使い分けながらテストを簡潔に記載する

テストランナーにはマッチャー(Matcher)1というAPIが存在する。これは、テストを実行するライブラリ(テストランナー)に「こんな検証をしてほしい」と伝えてテストを実行してもらうための関数である。マッチャーの使い方を知っていると、初めて出てくる仕様でも臆せずテストを書ける。

迷ったらtoBe()とtoEqual()で書く

このマッチャーを用途に応じて使い分けることで、検証内容を簡潔に伝えられる。こうすると次の人が修正を行うとき、アプリケーション仕様の解読を効率よく進められる。しかし、マッチャーは大量に存在する2ため、「どのマッチャーを使えば良いかわからない」場面が発生する。

ここでテストを諦めてしまうと本末転倒になってしまう。マッチャーがわからない場合は「一致する」ことを検証するマッチャーを利用してテストを書けば良い。「〇〇であること」の「〇〇」が配列、またはオブジェクトのときはtoEqual()3を使う。それ以外のときはtoBe()を使えば良い。

例えば、「h2タグが2個存在すること」を検証したい場合、検証対象はNumber(数)である。要素の数は要素の長さと言い換えられる。よって、要素のlength(長さ)がtoBe(2)(2つ存在する状態)であることを検証できれば良い。

次のテストケースは2つとも同じ内容を検証している。

it("h2タグが2つ存在する", () => {
  const content = render(<Component />);
  // h2タグが2つ存在することを確認
  expect(content.getAllByRole("heading", { level: 2 }).length).toBe(2);

  // toHaveLength()を使うと次のようにも記載できる。内容はh2タグが2つ存在することを確認している。
  expect(content.getAllByRole("heading", { level: 2 })).toHaveLength(2);
});

最初から完璧に綺麗なものを書こうとして挫折するくらいであれば、仕様を愚直に表現して書き進める方がずっと良い。 仕様やマッチャーに対する理解度が深まったタイミングでテストケースをリファクタリングすれば良いからである。とにかくテストがないよりはずっとマシである。

jest-domやJestのマッチャーを活用して仕様を表現する

Testing Libraryにはレンダリング結果(DOM)を取得し、それを元にテスト結果を判定するマッチャープラグインが存在する。 jest-dom4というプラグインで、Jestの実行環境向けのマッチャーである。

jest-domを入れることで、テストコードからも何の要素がどのような挙動になっているのか読み取りやすくなる。

JestのマッチャーもtoBe・toEqual以外を活用できるとテストできる範囲が広がる。 UIコンポーネントテストで出てきやすいパターンをいくつか挙げる。

DOMイベントが発火したことを検証したい

propsを用いて親子間で状態をやり取りするUIコンポーネントがあったとする。 そのような場面では「ボタンをクリックしたとき親コンポーネントに値を送信できたか」検証したいことが多々ある。

イベント自体が発火したか、はtoHaveBeenCalled5を使うと良い。

値が送信できたか確かめたい場合はtoHaveBeenCalledWith6を使う。

HTML要素の属性(attribute)の状態を検証したい

disabledのように、HTML属性がユーザー操作に影響を与えることがある。すると「Aの状態のときは指定の要素がdisabledか」をテストしたくなる。このような場合はtoHaveAttribute7を使う。jest-domプラグインで提供されているマッチャーなので次のようにインポートしないと動作しない。

// テストコードの先頭またはsetupTests.tsファイルにインポート構文を記載する
import "@testing-library/jest-dom";

記載するときは属性名 → 実際に確かめたい値の順に書く。次の例はdata-icon属性がmusicであることを確認するケースである。

  // data-icon属性がimageになっていることを確認
  expect(image).toHaveAttribute("data-icon", "music");

他にもあるが、大体この辺りのマッチャーを網羅すれば大半のテストケースを書ける。仕様が出揃ったら最後に適切なマッチャーに変更すれば良い。