EXERCISE 90分

演習:本格テストパイプライン構築

ストーリー

「ここまでの知識を総動員して、本番に使える テスト自動化パイプラインを構築しよう」

木村先輩が要件を書き出した。

「マトリクスビルド、キャッシュ、シークレット、アーティファクト。 全部組み合わせて、実戦レベルのCIパイプラインを作ってくれ。 これが完成すれば、うちのチームのデプロイ品質は 飛躍的に向上するはずだ」


演習の概要

Node.js + PostgreSQLのWebアプリケーションに対して、本格的なテスト自動化パイプラインを構築してください。

プロジェクト構成

webapp/
├── .github/
│   └── workflows/
│       └── ci.yml           ← 今回作成
├── src/
│   ├── index.ts
│   ├── routes/
│   ├── services/
│   └── repositories/
├── tests/
│   ├── unit/
│   │   ├── services.test.ts
│   │   └── validators.test.ts
│   ├── integration/
│   │   └── api.test.ts
│   └── e2e/
│       └── app.e2e.test.ts
├── package.json
├── tsconfig.json
├── jest.config.ts
├── Dockerfile
└── docker-compose.yml

課題1: マルチバージョンテストパイプライン

Node.js 18と20でテストを実行するマトリクスビルドを構築してください。

要件

  • マトリクス: Node.js 18, 20
  • キャッシュ: npm キャッシュを使用
  • ステップ: checkout → setup-node → npm ci → lint → typecheck → 単体テスト
  • fail-fast: false
  • タイムアウト: 15分
<details> <summary>解答例(自分で実装してから確認しよう)</summary>
yaml
name: CI Pipeline

on:
  pull_request:
    branches: [ main ]
  push:
    branches: [ main ]

permissions:
  contents: read

jobs:
  lint-and-unit-test:
    name: Lint & Unit Test (Node ${{ matrix.node-version }})
    runs-on: ubuntu-latest
    timeout-minutes: 15
    strategy:
      fail-fast: false
      matrix:
        node-version: [18, 20]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npm run lint

      - name: Type check
        run: npm run typecheck

      - name: Run unit tests
        run: npm run test:unit -- --coverage

      - name: Upload coverage report
        if: matrix.node-version == 20
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7
</details>

課題2: 結合テスト(サービスコンテナ付き)

PostgreSQLサービスコンテナを使った結合テストを追加してください。

要件

  • 課題1のテストが成功した後に実行(needs)
  • PostgreSQL 16 のサービスコンテナを使用
  • ヘルスチェック設定を含む
  • 環境変数 DATABASE_URL でDB接続先を指定
<details> <summary>解答例(自分で実装してから確認しよう)</summary>
yaml
  integration-test:
    name: Integration Test
    needs: lint-and-unit-test
    runs-on: ubuntu-latest
    timeout-minutes: 20

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

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run database migrations
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
        run: npm run db:migrate

      - name: Seed test data
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
        run: npm run db:seed

      - name: Run integration tests
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
          NODE_ENV: test
        run: npm run test:integration

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: integration-test-results
          path: test-results/
          retention-days: 7
</details>

課題3: ビルドとDockerイメージ作成

結合テスト成功後にアプリケーションをビルドし、Dockerイメージを作成してください。

要件

  • 結合テスト成功後に実行
  • npm run build でアプリをビルド
  • Docker イメージをビルド(pushはしない)
  • ビルド成果物をアーティファクトとして保存
  • バージョン情報をジョブ出力として公開
<details> <summary>解答例(自分で実装してから確認しよう)</summary>
yaml
  build:
    name: Build Application
    needs: integration-test
    runs-on: ubuntu-latest
    timeout-minutes: 15
    outputs:
      version: ${{ steps.version.outputs.version }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build application
        run: npm run build

      - name: Get version
        id: version
        run: |
          VERSION=$(node -p "require('./package.json').version")
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "Application version: $VERSION"

      - name: Build Docker image
        run: |
          docker build \
            --tag webapp:${{ steps.version.outputs.version }} \
            --tag webapp:${{ github.sha }} \
            --label "org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}" \
            --label "org.opencontainers.image.revision=${{ github.sha }}" \
            .

      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: production-build
          path: dist/
          retention-days: 3

      - name: Build summary
        run: |
          echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Item | Value |" >> $GITHUB_STEP_SUMMARY
          echo "|------|-------|" >> $GITHUB_STEP_SUMMARY
          echo "| Version | ${{ steps.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Commit | ${{ github.sha }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Docker Image | webapp:${{ steps.version.outputs.version }} |" >> $GITHUB_STEP_SUMMARY
</details>

課題4: 通知ジョブの追加

パイプライン全体の結果を通知するジョブを追加してください。

要件

  • buildジョブの完了後に実行
  • 常に実行(if: always()
  • 成功/失敗に応じたメッセージをJob Summaryに出力
  • 失敗時はバージョン情報やコミットSHAを含むデバッグ情報を出力
<details> <summary>解答例(自分で実装してから確認しよう)</summary>
yaml
  notify:
    name: Pipeline Result
    needs: [lint-and-unit-test, integration-test, build]
    if: always()
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Check pipeline result
        run: |
          echo "## Pipeline Result" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          if [ "${{ needs.lint-and-unit-test.result }}" == "success" ] && \
             [ "${{ needs.integration-test.result }}" == "success" ] && \
             [ "${{ needs.build.result }}" == "success" ]; then
            echo "### All checks passed!" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "| Stage | Result |" >> $GITHUB_STEP_SUMMARY
            echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
            echo "| Lint & Unit Test | ${{ needs.lint-and-unit-test.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "| Integration Test | ${{ needs.integration-test.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "| Build | ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "Version: ${{ needs.build.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          else
            echo "### Pipeline failed!" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "| Stage | Result |" >> $GITHUB_STEP_SUMMARY
            echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
            echo "| Lint & Unit Test | ${{ needs.lint-and-unit-test.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "| Integration Test | ${{ needs.integration-test.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "| Build | ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "**Debug Info:**" >> $GITHUB_STEP_SUMMARY
            echo "- Commit: ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY
            echo "- Actor: ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
            echo "- Branch: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
            exit 1
          fi
</details>

達成度チェック

課題内容完了
課題1マルチバージョンテスト(マトリクスビルド)[ ]
課題2結合テスト(サービスコンテナ付き)[ ]
課題3ビルドとDockerイメージ作成[ ]
課題4通知ジョブの追加[ ]

まとめ

ポイント内容
マトリクスビルド複数Node.jsバージョンで並列テスト
サービスコンテナPostgreSQLを使った結合テスト
アーティファクトビルド成果物の保存とジョブ間受け渡し
通知パイプライン結果のサマリー出力

チェックリスト

  • マトリクスビルドでマルチバージョンテストを構成できた
  • サービスコンテナを使った結合テストを実装できた
  • Dockerイメージのビルドとアーティファクト保存を実装できた
  • パイプライン全体の結果通知を実装できた
  • 完成したパイプラインの実行フローを説明できる

次のステップへ

演習が完了したら、Step 3 のチェックポイントクイズに挑戦しましょう。


推定所要時間: 90分