LESSON 30分

テスト戦略とCI

ストーリー

「CIパイプラインの核心はテストだ。テスト戦略なしにCIは成り立たない」

木村先輩がテストレポートを画面に表示した。

「うちのプロジェクトでは、テストカバレッジ80%を必須にしている。 でも大事なのは数字じゃない。どの種類のテストを、 いつ、どれだけ実行するかの戦略だ」

「戦略......具体的にはどう設計するんですか?」

「テストピラミッドをベースに、CIの各段階で実行するテストを 明確に分けるんだ。速いテストは毎回、遅いテストは必要な時だけ。 この使い分けが、開発速度と品質のバランスを保つ鍵になる」


テストの種類とCIでの配置

テストピラミッドとCIパイプライン

テストピラミッド × CIパイプライン

        /\
       /  \         E2Eテスト
      / E2E\        → デプロイ前のみ実行
     /------\       → ブラウザ操作を自動化
    / 結合    \     結合テスト
   / テスト    \    → PRマージ時に実行
  /------------\    → API呼び出し、DB接続を含む
 /  単体テスト    \  単体テスト
/________________\  → 毎push/毎PRで実行
                    → 外部依存なし、高速

CIパイプラインでの実行タイミング

テスト種類実行タイミング実行時間目安ツール例
Lint/静的解析毎push1〜2分ESLint, Prettier
単体テスト毎push2〜5分Jest, Vitest
結合テストPR作成・更新時5〜10分Supertest, TestContainers
E2Eテストmainへのマージ前10〜30分Playwright, Cypress
パフォーマンステスト定期実行(schedule)15〜60分k6, Artillery
セキュリティスキャン定期実行(schedule)5〜15分Snyk, Trivy

CIでのテスト自動化パターン

パターン1: 段階的テスト実行

yaml
name: Staged Testing

on:
  pull_request:
    branches: [ main ]

jobs:
  # Stage 1: 最速のチェック
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck

  # Stage 2: 単体テスト
  unit-test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run test:unit -- --coverage
      - name: Check coverage threshold
        run: |
          COVERAGE=$(cat coverage/coverage-summary.json | \
            jq '.total.lines.pct')
          echo "Coverage: ${COVERAGE}%"
          if (( $(echo "$COVERAGE < 80" | bc -l) )); then
            echo "Coverage below 80%!"
            exit 1
          fi

  # Stage 3: 結合テスト
  integration-test:
    needs: unit-test
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: test
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - name: Run integration tests
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/test
        run: npm run test:integration

サービスコンテナの活用

CIでデータベースやキャッシュなどの外部サービスを使うには、services を定義します。

yaml
services:
  postgres:
    image: postgres:16
    env:
      POSTGRES_DB: test
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - 5432:5432
    options: >-
      --health-cmd pg_isready
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

  redis:
    image: redis:7
    ports:
      - 6379:6379
    options: >-
      --health-cmd "redis-cli ping"
      --health-interval 10s
      --health-timeout 5s
      --health-retries 5

テストカバレッジの管理

カバレッジレポートの生成と確認

yaml
- name: Run tests with coverage
  run: npm test -- --coverage --coverageReporters=json-summary

- name: Display coverage summary
  run: |
    echo "## Coverage Report" >> $GITHUB_STEP_SUMMARY
    echo "" >> $GITHUB_STEP_SUMMARY
    echo "| Metric | Coverage |" >> $GITHUB_STEP_SUMMARY
    echo "|--------|----------|" >> $GITHUB_STEP_SUMMARY
    cat coverage/coverage-summary.json | jq -r '
      .total | to_entries[] |
      "| \(.key) | \(.value.pct)% |"
    ' >> $GITHUB_STEP_SUMMARY

カバレッジの閾値設定

json
// jest.config.js
{
  "coverageThreshold": {
    "global": {
      "branches": 80,
      "functions": 80,
      "lines": 80,
      "statements": 80
    }
  }
}

Flakyテスト対策

不安定なテスト(Flaky Test)はCI の信頼性を損ないます。

Flakyテストの原因と対策

原因対策
タイミング依存明示的なwaitやリトライ機構を使う
外部サービス依存モックやサービスコンテナを使う
テスト間の依存各テストを独立させる(setupで初期化)
ランダムなデータシード値を固定する
リソース競合並列実行時のポート/ファイル衝突を避ける

リトライ戦略

yaml
- name: Run tests with retry
  uses: nick-fields/retry@v3
  with:
    timeout_minutes: 10
    max_attempts: 3
    command: npm test

まとめ

ポイント内容
テストピラミッド単体テストを多く、E2Eテストは少なく
段階的実行速いテストを先に、遅いテストは後に
サービスコンテナDB/キャッシュをCIで使う方法
Flakyテスト不安定なテストはCI信頼性の敵

チェックリスト

  • テストピラミッドに基づくCI戦略を設計できる
  • サービスコンテナを使ったテスト環境の構築方法を理解した
  • テストカバレッジの閾値設定と確認方法を把握した
  • Flakyテストの原因と対策を説明できる

次のステップへ

次のセクションでは、マトリクスビルドと並列実行を学びます。 複数のNode.jsバージョンやOSで同時にテストを走らせ、パイプラインの効率を最大化しましょう。


推定読了時間: 30分