LESSON 25分

テストの種類と戦略

ストーリー

「テストにはいくつかの種類がある。 全てを理解した上で、戦略的にテストを配置することが大事だ」

松本先輩がホワイトボードにピラミッドを描いた。

「テストピラミッド。これがテスト戦略の基本だ。 底辺が厚く、頂点が薄い。この形を覚えておけ」


テストの種類

1. ユニットテスト(単体テスト)

関数やクラスなど、最小単位のコードを個別にテストします。

typescript
// テスト対象
function add(a: number, b: number): number {
  return a + b;
}

// ユニットテスト
describe('add', () => {
  it('正の数同士の加算', () => {
    expect(add(2, 3)).toBe(5);
  });

  it('負の数を含む加算', () => {
    expect(add(-1, 5)).toBe(4);
  });

  it('0 との加算', () => {
    expect(add(0, 0)).toBe(0);
  });
});
特徴内容
対象関数、メソッド、クラス
速度非常に高速(ミリ秒)
依存外部依存はモックで置換
信頼性高い(決定的)
コスト低い

2. インテグレーションテスト(結合テスト)

複数のコンポーネントが正しく連携するかテストします。

typescript
// インテグレーションテスト: サービス + リポジトリ
describe('UserService + Database', () => {
  let userService: UserService;

  beforeAll(async () => {
    await setupTestDatabase();
    userService = new UserService(new UserRepository());
  });

  it('ユーザーを作成して取得できる', async () => {
    const created = await userService.createUser({
      name: '田中太郎',
      email: 'tanaka@example.com',
    });

    const found = await userService.findById(created.id);
    expect(found?.name).toBe('田中太郎');
    expect(found?.email).toBe('tanaka@example.com');
  });

  afterAll(async () => {
    await cleanupTestDatabase();
  });
});
特徴内容
対象複数コンポーネントの連携
速度中程度(秒単位)
依存DB、外部APIなどの実際の依存を使用
信頼性中程度(環境依存の場合あり)
コスト中程度

3. E2Eテスト(エンドツーエンドテスト)

ユーザーの操作をシミュレートし、システム全体をテストします。

typescript
// Playwright による E2E テスト
import { test, expect } from '@playwright/test';

test('ユーザーがログインして注文できる', async ({ page }) => {
  // ログイン
  await page.goto('/login');
  await page.fill('[data-testid="email"]', 'user@example.com');
  await page.fill('[data-testid="password"]', 'password123');
  await page.click('[data-testid="login-button"]');

  // 商品をカートに追加
  await page.goto('/products/1');
  await page.click('[data-testid="add-to-cart"]');

  // 注文完了
  await page.goto('/cart');
  await page.click('[data-testid="checkout"]');
  await expect(page.locator('[data-testid="order-success"]')).toBeVisible();
});
特徴内容
対象システム全体(UI → API → DB)
速度遅い(分単位)
依存全てのシステムが必要
信頼性低い(フレーキーになりやすい)
コスト高い

テストピラミッド

          ╱╲
         ╱  ╲
        ╱ E2E╲          少ない(重要シナリオのみ)
       ╱  10% ╲
      ╱────────╲
     ╱結合テスト╲        中程度
    ╱   20%     ╲
   ╱──────────────╲
  ╱ ユニットテスト  ╲     多い(関数・クラス単位)
 ╱      70%         ╲
╱────────────────────╲

 速い ←─── 実行速度 ───→ 遅い
 安い ←─── コスト   ───→ 高い
 高い ←─── 安定性   ───→ 低い

なぜピラミッド型が最適なのか

割合理由
ユニットテスト70%高速、安定、保守しやすい、問題の特定が容易
インテグレーションテスト20%コンポーネント間の連携を検証
E2Eテスト10%重要なユーザーシナリオのみ。遅くて不安定

アンチパターン:逆ピラミッド(アイスクリームコーン)

 ┌────────────────────┐
 │     手動テスト       │  ← 大量の手動テスト
 ├────────────────────┤
 │    E2Eテスト         │  ← 大量のE2E
 ├──────────┤
 │ 結合テスト │
 ├────┤
 │UT  │              ← ユニットテストがほぼない
 └────┘

→ テスト実行が遅い、不安定、保守コストが高い

テスト戦略の設計

何をどのレベルでテストするか

ユニットテストでテストすべき:
├── ビジネスロジック(計算、バリデーション)
├── Value Object の振る舞い
├── ユーティリティ関数
└── 状態遷移のロジック

インテグレーションテストでテストすべき:
├── DB との CRUD 操作
├── API エンドポイントの入出力
├── 外部サービスとの連携
└── 認証・認可のフロー

E2Eテストでテストすべき:
├── ユーザー登録〜ログイン
├── 商品検索〜購入の一連の流れ
├── 決済処理の正常完了
└── クリティカルなビジネスフロー

まとめ

テスト種類対象速度コスト推奨割合
ユニットテスト関数・クラス非常に高速70%
インテグレーションテストコンポーネント連携中程度20%
E2Eテストシステム全体遅い10%

チェックリスト

  • 3種類のテストの違いを説明できる
  • テストピラミッドの形と理由を説明できる
  • 逆ピラミッド(アイスクリームコーン)の問題を理解した
  • 何をどのレベルでテストすべきか判断できる

次のステップへ

テストの種類を理解したら、次はテストケース設計技法を学びます。境界値分析、同値分割、デシジョンテーブルなどの体系的な手法を身につけましょう。


推定読了時間: 25分