Skip to main content

テスト戦略

テストを書く意味・目的

テストを書くことは、ソフトウェア開発において非常に重要である。その主な目的と意義は以下の通り。

a) 品質保証

  • バグの早期発見と修正
  • 仕様との整合性の確認
  • ユーザー体験の向上

b) 開発効率の向上

  • リグレッションの防止
  • リファクタリングの安全性確保
  • 開発者の自信向上

c) ドキュメンテーション

  • コードの使用方法や期待される動作の明確化
  • チーム内でのコミュニケーション促進

d) 設計の改善

  • テスタビリティの高いコード設計の促進
  • コンポーネントの責務の明確化

バグが発生しやすい状況

a) コードの変更や機能追加時

  • 既存の機能に影響を与える可能性
  • 新旧コードの相互作用による予期せぬ動作

b) 異なる環境での動作時

  • ブラウザの違いによる挙動の差異
  • デバイスのスペックや画面サイズの違いによる問題

c) ユーザーの予期せぬ操作時

  • 想定外の入力値や操作順序
  • 高負荷時や低ネットワーク環境での使用

d) 外部APIとの連携時

  • APIの仕様変更や障害
  • データ形式の不一致

不正な状態を防ぐためのテスト

a) 入力値のバリデーション

  • 必須項目のチェック
  • 形式や範囲の検証

b) エラー処理とエッジケース

  • 境界値テスト
  • エラー時の適切なフィードバック

c) 状態管理の一貫性

  • コンポーネント間の状態の整合性
  • 非同期処理後の状態更新の検証

d) セキュリティ関連のチェック

  • XSS攻撃の防止
  • CSRF対策の確認

テストの種類

静的テスト

目的:コードの品質と一貫性を保つ

手法:

  • TypeScriptの型チェック:型の不一致や潜在的なエラーを事前に検出
  • ESLintによるコード品質チェック:コーディング規約の遵守、潜在的なバグの検出
  • Prettierによるコードフォーマット:コードスタイルの統一

主なツール:

  • TypeScript Compiler
  • ESLint(カスタムルールの設定)
  • Prettier

単体テスト

目的:個々のコンポーネントや関数の正確性を確認

対象:

  • Reactコンポーネント
  • ユーティリティ関数
  • カスタムフック

テスト内容:

  • プロップスの受け渡し
  • イベントハンドリング
  • 状態の更新
  • 条件付きレンダリング
  • ビジュアル(UI/UX)

主なツール:

  • Jest
  • React Testing Library
  • @testing-library/react-hooks
  • Storybook

結合テスト

目的:複数のコンポーネントやモジュール間の連携を確認

対象:

  • 複数のコンポーネントで構成される機能
  • コンテキストやReduxとの連携
  • APIとの連携

テスト内容:

  • データの流れ
  • 状態の共有と更新
  • エラー処理
  • 非同期処理
  • ビジュアル(UI/UX)

主なツール:

  • React Testing Library
  • MSW (Mock Service Worker)
  • Storybook

E2Eテスト

目的:実際のユーザー体験に近い形で全体的な機能を検証

対象:

  • 主要なユーザーフロー
  • クリティカルな機能
  • 複数ページにまたがる操作

テスト内容:

  • サインインからサインアウトまでの一連の流れ
  • 検索機能や絞り込み機能
  • フォーム送信と結果の確認

主なツール:

  • Cypress
  • Playwright

テスト戦略モデル

アイスクリームコーン型

概要:

  • E2Eテストに最も重点を置くモデル
  • 上位のテスト(E2Eテスト、結合テスト)の比率が高い
  • 下位のテスト(単体テスト、静的テスト)の比率が低い

特徴:

  • ユーザー体験に近いテストを重視
  • システム全体の動作確認に焦点を当てる
  • 早期のフィードバックが難しい
  • テストの実行時間が長くなりがち

適している状況:

  • レガシーシステムの保守
  • 小規模なプロジェクト
  • 迅速なプロトタイピングが必要な場合

テストピラミッド型

概要:

  • 下位のテスト(単体テスト、静的テスト)に重点を置くモデル
  • 上位に行くほどテストの数が少なくなる
  • 単体テストが最も多く、E2Eテストが最も少ない

特徴:

  • 高速で頻繁なフィードバックが可能
  • 下位レベルでの問題を早期に発見できる
  • テストの保守が比較的容易
  • コスト効率が良い

適している状況:

  • 大規模で複雑なプロジェクト
  • 継続的デリバリーを実践している開発チーム
  • マイクロサービスアーキテクチャ

テスティングトロフィー型

概要:

  • 静的テストに最も重点を置き、他のテストタイプもバランスよく配分するモデル
  • テストピラミッドを進化させたモデル

特徴:

  • 静的テストによる早期の問題発見
  • 単体テストと結合テストのバランスが良い
  • E2Eテストも適度に含まれる
  • モダンな開発プラクティスに適合

適している状況:

  • フロントエンド開発(特にReactやVueなどのコンポーネントベースのフレームワーク)
  • アジャイル開発
  • 品質と開発速度のバランスを重視するプロジェクト

理想のテスト戦略

  • ロジックは基本的に hooks に切り出す
  • hooks は基本的に単体テスト書く

静的テスト

  • 全てのTypeScriptファイルに対して厳格な型チェックを適用
  • ESLintルールセットをカスタマイズし、プロジェクト固有のルールを追加
  • Prettierの設定をチーム内で統一し、コードスタイルの一貫性を確保
  • CIでの自動チェックを実施し、問題がある場合はビルドを失敗させる

単体テスト

  • 全てのReactコンポーネントに対して、最低限のレンダリングテストを実施
  • ロジックを含むコンポーネントは、各条件分岐をカバーするテストを作成
  • ユーティリティ関数は、入力値の範囲や特殊なケースを考慮したテストを実施
  • カスタムフックは、異なる使用シナリオでのテストを行う
  • モックやスタブを適切に使用し、外部依存を分離
  • StorybookでコンポーネントのUIの確認を行う

結合テスト

  • 主要な画面やフローに対して、複数のコンポーネントの連携をテスト
  • Recoilやコンテキストを使用している場合、状態の更新と反映をテスト
  • APIとの連携は、MSWを使用してモックレスポンスを返すテストを実施
  • エラーケースや読み込み中の状態なども考慮したテストを作成

E2Eテスト

  • サインインなど主要な機能の利用など、重要なフローをテスト
  • 複数のデバイスサイズや主要なブラウザでのテストを実施

理想のテスト実施方針

  • 新機能開発時:

    • 機能仕様に基づいたテストケースを事前に作成
    • TDD(テスト駆動開発)の適用を推奨
    • 単体テストと結合テストを必須とし、機能の重要度に応じてE2Eテストも追加
    • コードレビュー時にテストの十分性も確認
  • バグ修正時:

    • バグを再現するテストケースを先に作成
    • 修正後、そのテストが通ることを確認
    • 関連する箇所の既存テストも確認し、必要に応じて追加や修正
  • リファクタリング時:

    • リファクタリング前に全てのテストが通ることを確認
    • リファクタリング後も全てのテストが通ることを再確認
    • テストコード自体のリファクタリングも定期的に実施
  • CIでの自動テスト実行:

    • プルリクエスト作成時に全てのテストを実行
    • メインブランチへのマージ時にも全テストを実行
    • テスト失敗時はマージをブロック
    • 定期的(例:毎晩)にE2Eテストを実行し、環境の変化による影響を早期に検出
  • パフォーマンステスト:

    • 重要な画面やコンポーネントに対して、定期的にパフォーマンス計測を実施
    • Lighthouseなどのツールを使用し、スコアの推移を監視
  • アクセシビリティテスト:

    • 開発中のコンポーネントに対して、aXeなどのツールを使用してチェック
    • 定期的に全画面に対してアクセシビリティ監査を実施