LESSON 25分

ストーリー

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

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

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

テストの種類

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

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

// テスト対象
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. インテグレーションテスト(結合テスト)

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

// インテグレーションテスト: サービス + リポジトリ
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テスト(エンドツーエンドテスト)

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

// 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)
速度遅い(分単位)
依存全てのシステムが必要
信頼性低い(フレーキーになりやすい)
コスト高い

テストピラミッド

graph TD
    E2E["E2Eテスト<br/>10%<br/>少ない(重要シナリオのみ)"]
    INT["結合テスト<br/>20%<br/>中程度"]
    UNIT["ユニットテスト<br/>70%<br/>多い(関数・クラス単位)"]

    E2E --- INT --- UNIT

    classDef e2e fill:#f8d7da,stroke:#dc3545,color:#000
    classDef int fill:#fff3cd,stroke:#ffc107,color:#000
    classDef unit fill:#d4edda,stroke:#28a745,color:#000
    class E2E e2e
    class INT int
    class UNIT unit

速い <--- 実行速度 ---> 遅い / 安い <--- コスト ---> 高い / 高い <--- 安定性 ---> 低い

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

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

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

graph TD
    MANUAL["手動テスト<br/>← 大量の手動テスト"]
    E2E["E2Eテスト<br/>← 大量のE2E"]
    INT["結合テスト"]
    UT["UT<br/>← ユニットテストがほぼない"]

    MANUAL --- E2E --- INT --- UT

    classDef bad fill:#f8d7da,stroke:#dc3545,color:#000
    classDef warn fill:#fff3cd,stroke:#ffc107,color:#000
    classDef small fill:#e2e3e5,stroke:#6c757d,color:#000
    class MANUAL bad
    class E2E bad
    class INT warn
    class UT small

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


テスト戦略の設計

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

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

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

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

まとめ

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

チェックリスト

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

次のステップへ

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


推定読了時間: 25分