テスト戦略
テストの仕事は、開発した差分が正しく動くと証明することです。差分から必要な検証を選ぶほか、単体テスト、API契約、アクセシビリティ、見た目の差分、性能など、目的が決まった検証を個別に実行します。テストが返すのは合否と証拠までで、製品全体を探索して別の改善を始めることはしません。
同じ判断を複数の層で重複させない
製品仕様と実装とテストは、変わらない仕様IDで結びつけて追跡します。そのうえで、検証する内容ごとに、どの層で証明するかを1つ決めます。
- 業務のルールは、ApplicationとDomain(アーキテクチャ)の単体テストで検証する
- HTTP契約は、エンドポイントのE2E(end-to-end。利用者の操作を最初から最後まで通しで確かめるテスト)で検証する
- 利用者の完了条件は、ユーザージャーニーのE2Eで検証する
- 全ルートの接続は、スモークテスト(起動と主要経路だけを軽く確かめるテスト)で検証する
同じ判断を複数の層で検証すると、仕様変更のたびに複数のテストが同時に壊れ、直すコストが検証の価値を上回っていきます。どの層で何を証明するかを決めておくことが、テストを資産のまま保つ条件だと考えています。
テストの使い分け
- 編集時のHookは、変更ファイルのformat、lint、近接するテストなど、短時間で終わる補助検査に限定する
- 単体テストは、純粋関数、Application・Domainの規則、コンポーネントの振る舞いを検証する
- API・CLI結合テストは、開発用の実際のデータベースやストレージを含む契約と利用箇所の接続を検証する。本番へは接続しない(データの事故)
- E2Eは、利用者が目的を完了できる一連の操作と認可境界を検証する
- ビルド成果物のスモークテストは、開発サーバでは見つからない本番ビルド固有の問題を検出する
- Storybookのスモークテストは、bundleの成功だけでなく、登録したstoryを実ブラウザで描画できることを検証する
- UI変更は、自動テストだけで終わらせず、実アプリを操作して状態、権限、画面幅ごとの結果を確認する
- 品質ゲートは、変更を外部へ反映する前に必要な静的検査、単体テスト、結合テストをまとめて実行する
検査の広げ方
すべての検査を編集のたびに実行すると、開発のフィードバックが遅くなります。編集時は速い検査、作業中は変更箇所に近いテスト、完了前とCIでは影響範囲を満たす検査、デプロイ前は品質ゲートという順に広げます。Hookによる自動検査は補助であり、完了前の検証を置き換えません。
モックと実DBの境界
単体テストでは、DBアクセスや外部サービスをモック(本物の処理の置き換え)にして計算や判断を検証します。SQL、保存処理、DBとの接続を検証する結合テストでは、テスト専用の実DBへ架空のデータを入れて確認します。保存処理までモックにすると、SQLやデータ構造の不整合が検査を通過するためです。
メール送信、決済、有料APIへの呼び出しはモックに置き換えます。接続自体を確認する場合は、外部サービスのテスト用環境と鍵を使います。どの層も本番DBへは接続せず、本番の顧客データを複製しません(モック)。
外部サービスへの呼び出しは、差し替えが必要な境界へ集めます。テスト対象の業務処理を変更せず、依存する接続先を差し替えられるかを確認します。モックで確認した範囲と、本物との接続で未確認の条件を、検証結果へ残します。
CIを必須チェックにする
CI(変更のたびに検査を実行する仕組み)は、実行するだけでは不合格の変更を止められません。弊社では、必要な検査をGitHubの必須チェックにし、失敗中のmergeを拒否します。デプロイも必要な検査の成功後にだけ進むようにします。
ブランチ保護やルールセットの対象、検査の実行条件、必須チェック名が一致していることを確認します。管理者などの例外権限や契約プランの制限も確認し、自動で止められず人の確認が残る箇所を明示します。
規約を検査するlintやテストも、統合前に必要なものは同じ必須チェックへ含めます。コマンド、対象、実行結果を示し、未実施の検査や既存の失敗を成功と区別します。