Jest を使ってスナップショットテストを行う

Jest は Facebook が中心になって開発している人気の JavaScript のテスティングフレームワークです。この記事では、 Jest の特徴的な機能であるスナップショットテストについて解説します。

スナップショットテストとは

システムの状態をある瞬間で切り取ったもの、あるいはバックアップのことをスナップショットと呼びますが、Jest におけるスナップショットの意味も同様です。

スナップショットテストでは、テストの対象になっている関数の出力をスナップショットとして保存し、コードの変更によってスナップショットに変更が生じているかどうかを検出(テスト)します。特に React などのフレームワークで UI を生成するコードに対するテストとしてスナップショットテストが行われていることが多いです。

スナップショットテストの何が嬉しいのか

UI を生成するためのコードは、複雑化しやすく、またユニットテストの対象となるロジックを抽出することが難しいものになりがちです。また、短いライフサイクルで UI の変更やリリースがあることを考えると、重厚なテストを実施しても開発のリソースの無駄遣いになってしまうことが懸念されます。

スナップショットテストを用いることで、最低限のリグレッションテスト、つまり、意図しない変更の発生を検出することができます。スナップショットテストは簡単に作成して実行することができるため、コストの面で優れています。

Jest で React コンポーネントのスナップショットを作成する

テスト環境のセットアップ

それでは実際にスナップショットテストを行ってみます。まずは React のスキャフォルディングツールの craeaste-react-app を利用して React の開発環境を作成します。この環境には Jest もデフォルトでインストールされています。

スキャフォルディングが終わったら、React コンポーネントのスナップショットを作成するために必要なツールの react-test-renderer をインストールします。

$ npx create-react-app sandbox-snapshot-testing
$ npm install react-test-render --save-dev

この時点でnpm start コマンドを使ってプロジェクトを実行し、正しく表示されることを確認しておきます。

$ npm start

テストコードの作成

テストコードを作成します。

src/App.test.js

import React from 'react'
import App from './App'
import renderer from 'react-test-renderer'

it('matches to the snapshot', () => {
  const tree = renderer.create().toJSON()
  expect(tree).toMatchSnapshot()
})

テストの実行

テストを実行します。

$ npm test

テストが成功しました。この時、__snapshots__ というディレクトリが作成され、コンポーネントのレンダリングの結果がスナップショットとして保存されています。

それではスナップショットテストをわざと失敗させてみます。 App.js のコンポーネントのコードを変更し、UI を更新します。

some fix という文字を追加して見た目を変更しています。この状態でテストを実行すると、

$ npm test

コンポーネントのレンダリングの結果がスナップショットとマッチせず、テストが意図通り失敗しました。
この UI の変更が意図したものであるときは、以下のコマンドでスナップショットを更新することができます。

$ npm test -- -u

スナップショットテストのワークフロー

__snapshots__ に作成されるスナップショットのデータのファイルは自動生成されたものです。自動生成ファイルを Git にコミットしないことは基本的な Git の約束事ではありますが、スナップショットのファイルは例外です。これらは Git にコミットして使います。 Git にスナップショットのデータがコミットされていることで、コミット毎にスナップショットテストの成功・失敗が保存されます。このことにより、 CI ツールなどとの連携が容易になります。