はじめに

TDD(テスト駆動開発)で書いたテストは、ローカル環境で実行するだけでは十分ではありません。チーム開発において真の価値を発揮するには、CI/CD(継続的インテグレーション/継続的デリバリー)パイプラインと連携させ、すべてのコード変更に対して自動的にテストを実行する仕組みが不可欠です。

本記事では、TDDとCI/CDを連携させることで得られるメリットから、GitHub Actionsを使った具体的な実装方法まで、実践的なガイドを提供します。JavaScript/TypeScript(Jest/Vitest)とJava(JUnit 5/Maven)の両方の例を示しながら、テストの自動実行、カバレッジレポートの生成、プルリクエストでのテスト必須化、テスト失敗時の通知設定まで、網羅的に解説します。

TDDとCI/CDを連携させるメリット

TDDとCI/CDの組み合わせは、単なる自動化を超えた相乗効果を生み出します。

相乗効果の全体像

graph TB
    subgraph TDD["TDD(テスト駆動開発)"]
        A[Red: 失敗するテスト] --> B[Green: テスト通過]
        B --> C[Refactor: リファクタリング]
        C --> A
    end
    
    subgraph CICD["CI/CD パイプライン"]
        D[コードプッシュ] --> E[自動ビルド]
        E --> F[テスト実行]
        F --> G[カバレッジ計測]
        G --> H{品質基準}
        H -->|Pass| I[マージ可能]
        H -->|Fail| J[マージ不可]
    end
    
    C --> D
    TDD -.->|品質の担保| CICD
    CICD -.->|即時フィードバック| TDD

TDD単体とCI/CD連携の比較

観点 TDD単体 TDD + CI/CD連携
テスト実行 開発者のローカルのみ 全コミットで自動実行
品質保証 開発者の意識に依存 仕組みで強制
レグレッション検知 マージ後に発覚 マージ前に検知
カバレッジ管理 手動確認 自動レポート・閾値設定
チーム全体の品質 ばらつきあり 一定水準を維持

具体的なメリット

1. レグレッションの早期発見

プルリクエスト時点で既存機能への影響を検知できるため、本番環境へのバグ混入を防止できます。

2. コードレビューの効率化

テストが通過していることを前提にレビューできるため、レビュアーはビジネスロジックや設計に集中できます。

3. リファクタリングへの自信

テストスイートがセーフティネットとして機能するため、大規模なリファクタリングにも安心して取り組めます。

4. ドキュメントとしてのテスト

CIで実行されるテストは、常に最新の仕様書として機能します。

GitHub Actionsの基礎

GitHub Actionsは、GitHubが提供するCI/CDプラットフォームです。リポジトリ内の.github/workflowsディレクトリにYAMLファイルを配置することで、ワークフローを定義できます。

ワークフローの基本構造

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
name: ワークフロー名

on:
  # トリガーイベント
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  job-name:
    runs-on: ubuntu-latest
    
    steps:
      - name: ステップ名
        uses: アクション名
        with:
          パラメータ: 

主要な構成要素

要素 説明
name ワークフローの名前 CI Test
on トリガーイベント push, pull_request
jobs 実行するジョブの定義 test, build
runs-on 実行環境 ubuntu-latest
steps ジョブ内のステップ チェックアウト、テスト実行
uses 再利用可能なアクション actions/checkout@v4
run シェルコマンド実行 npm test

JavaScript/TypeScriptプロジェクトのCI設定

Jest を使用したNode.jsプロジェクト

.github/workflows/test.ymlを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Test

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: ['18.x', '20.x']
    
    steps:
      - name: Checkout repository
        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 tests
        run: npm test
      
      - name: Run tests with coverage
        run: npm run test:coverage

カバレッジレポートの生成と保存

Jestのカバレッジ機能を活用し、結果をアーティファクトとして保存します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Test with Coverage

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests with coverage
        run: npm run test:coverage
      
      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 30

package.jsonのスクリプト設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage --coverageReporters=text --coverageReporters=lcov"
  },
  "jest": {
    "collectCoverageFrom": [
      "src/**/*.{js,ts}",
      "!src/**/*.d.ts",
      "!src/**/index.{js,ts}"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 80,
        "functions": 80,
        "lines": 80,
        "statements": 80
      }
    }
  }
}

Vitest を使用したプロジェクト

Viteベースのプロジェクトでは、Vitestを使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
name: Vitest Test

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run Vitest
        run: npm run test:ci
      
      - name: Run Vitest with coverage
        run: npm run test:coverage

vitest.config.tsの設定例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov', 'html'],
      exclude: [
        'node_modules/',
        'test/',
        '**/*.d.ts',
        '**/*.config.*'
      ],
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 80,
        statements: 80
      }
    }
  }
})

JavaプロジェクトのCI設定

Maven + JUnit 5 の基本設定

.github/workflows/maven-test.ymlを作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
name: Java CI with Maven

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: maven
      
      - name: Run tests with Maven
        run: mvn --batch-mode --update-snapshots verify
      
      - name: Upload test results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: target/surefire-reports/
          retention-days: 30

マトリックスビルドで複数JDKバージョンをテスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
name: Java CI Matrix

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        java-version: ['17', '21']
        include:
          - java-version: '17'
            maven-args: '-P java17'
          - java-version: '21'
            maven-args: '-P java21'
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Set up JDK ${{ matrix.java-version }}
        uses: actions/setup-java@v4
        with:
          java-version: ${{ matrix.java-version }}
          distribution: 'temurin'
          cache: maven
      
      - name: Run tests
        run: mvn --batch-mode verify ${{ matrix.maven-args }}

JaCoCoによるカバレッジ計測

pom.xmlにJaCoCoプラグインを追加します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.12</version>
    <executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>verify</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>check</id>
            <goals>
                <goal>check</goal>
            </goals>
            <configuration>
                <rules>
                    <rule>
                        <element>BUNDLE</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                            <limit>
                                <counter>BRANCH</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>0.80</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

ワークフローでカバレッジレポートをアップロードします。

1
2
3
4
5
6
- name: Upload JaCoCo coverage report
  uses: actions/upload-artifact@v4
  with:
    name: jacoco-report
    path: target/site/jacoco/
    retention-days: 30

カバレッジレポートの可視化

Codecovとの連携

Codecovを使用すると、カバレッジの推移を可視化し、プルリクエストにコメントを自動追加できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
name: Test with Codecov

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests with coverage
        run: npm run test:coverage
      
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./coverage/lcov.info
          flags: unittests
          name: codecov-umbrella
          fail_ci_if_error: true

プルリクエストへのカバレッジコメント

カバレッジ結果をプルリクエストに自動コメントする設定です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
- name: Code Coverage Summary Report
  uses: irongut/CodeCoverageSummary@v1.3.0
  with:
    filename: coverage/cobertura-coverage.xml
    badge: true
    format: markdown
    output: both
    thresholds: '60 80'

- name: Add Coverage PR Comment
  uses: marocchino/sticky-pull-request-comment@v2
  if: github.event_name == 'pull_request'
  with:
    recreate: true
    path: code-coverage-results.md

プルリクエストでのテスト必須化

ブランチ保護ルールの設定

GitHub Actionsのテストをマージの必須条件として設定します。

flowchart LR
    A[PR作成] --> B[CI実行]
    B --> C{テスト結果}
    C -->|Pass| D[ステータスチェック通過]
    C -->|Fail| E[ステータスチェック失敗]
    D --> F[マージ可能]
    E --> G[マージ不可]
    
    style D fill:#28a745
    style E fill:#dc3545
    style F fill:#28a745
    style G fill:#dc3545

設定手順

  1. リポジトリの「Settings」を開く
  2. 「Branches」を選択
  3. 「Add branch protection rule」をクリック
  4. 以下を設定する
設定項目 推奨値
Branch name pattern main
Require a pull request before merging 有効
Require status checks to pass before merging 有効
Require branches to be up to date before merging 有効
Status checks that are required test(ワークフローのjob名)

ステータスチェックの設定例

ワークフローのジョブ名が、ブランチ保護で指定する名前になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: CI

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

jobs:
  # このジョブ名「test」をブランチ保護で指定
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      - run: npm ci
      - run: npm test
  
  # Lintチェックも必須にする場合
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      - run: npm ci
      - run: npm run lint

テスト失敗時の通知設定

Slack通知

テスト失敗時にSlackへ通知を送信します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
name: Test with Slack Notification

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        id: test
        run: npm test
      
      - name: Notify Slack on failure
        if: failure()
        uses: slackapi/slack-github-action@v1.26.0
        with:
          payload: |
            {
              "text": "Test Failed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Test Failed* :x:\n*Repository:* ${{ github.repository }}\n*Branch:* ${{ github.ref_name }}\n*Commit:* ${{ github.sha }}\n*Author:* ${{ github.actor }}"
                  }
                },
                {
                  "type": "actions",
                  "elements": [
                    {
                      "type": "button",
                      "text": {
                        "type": "plain_text",
                        "text": "View Workflow"
                      },
                      "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
                    }
                  ]
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

GitHub Issues への自動起票

テスト失敗時にIssueを自動作成する設定です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- name: Create Issue on Test Failure
  if: failure() && github.ref == 'refs/heads/main'
  uses: actions/github-script@v7
  with:
    script: |
      const title = `Test failure in ${context.sha.substring(0, 7)}`;
      const body = `
      ## Test Failure Report
      
      - **Commit:** ${context.sha}
      - **Branch:** ${context.ref}
      - **Author:** ${context.actor}
      - **Workflow Run:** ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}
      
      Please investigate and fix the failing tests.
      `;
      
      await github.rest.issues.create({
        owner: context.repo.owner,
        repo: context.repo.repo,
        title: title,
        body: body,
        labels: ['bug', 'test-failure']
      });

実践的なワークフロー構成例

フルスタックプロジェクトの完全なCI設定

フロントエンド(React + Vitest)とバックエンド(Java + Maven)を含むプロジェクトの例です。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
name: Full Stack CI

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

env:
  NODE_VERSION: '20.x'
  JAVA_VERSION: '21'

jobs:
  # フロントエンドのテスト
  frontend-test:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./frontend
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
          cache-dependency-path: frontend/package-lock.json
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run linter
        run: npm run lint
      
      - name: Run type check
        run: npm run type-check
      
      - name: Run unit tests
        run: npm run test:ci
      
      - name: Run tests with coverage
        run: npm run test:coverage
      
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./frontend/coverage/lcov.info
          flags: frontend
          fail_ci_if_error: true

  # バックエンドのテスト
  backend-test:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./backend
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: ${{ env.JAVA_VERSION }}
          distribution: 'temurin'
          cache: maven
      
      - name: Run tests with Maven
        run: mvn --batch-mode verify
      
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: ./backend/target/site/jacoco/jacoco.xml
          flags: backend
          fail_ci_if_error: true

  # E2Eテスト(両方のテストが通過後に実行)
  e2e-test:
    needs: [frontend-test, backend-test]
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: ${{ env.JAVA_VERSION }}
          distribution: 'temurin'
          cache: maven
      
      - name: Start backend
        run: |
          cd backend
          mvn spring-boot:run &
          sleep 30
      
      - name: Run E2E tests
        run: |
          cd frontend
          npm ci
          npm run test:e2e

  # 全テスト通過後のサマリー
  test-summary:
    needs: [frontend-test, backend-test, e2e-test]
    runs-on: ubuntu-latest
    if: always()
    
    steps:
      - name: Check test results
        run: |
          if [ "${{ needs.frontend-test.result }}" == "failure" ] || \
             [ "${{ needs.backend-test.result }}" == "failure" ] || \
             [ "${{ needs.e2e-test.result }}" == "failure" ]; then
            echo "Some tests failed"
            exit 1
          fi
          echo "All tests passed successfully"

TDDサイクルとCIの統合ワークフロー

TDDをチーム開発で実践する際の理想的なワークフローを示します。

sequenceDiagram
    participant Dev as 開発者
    participant Local as ローカル環境
    participant Git as Git/GitHub
    participant CI as GitHub Actions
    participant Review as レビュアー
    
    Dev->>Local: 1. Redテスト作成
    Local->>Local: テスト失敗確認
    Dev->>Local: 2. Green実装
    Local->>Local: テスト通過確認
    Dev->>Local: 3. Refactorリファクタリング
    Local->>Local: テスト通過確認
    Dev->>Git: 4. コミット・プッシュ
    Git->>CI: 5. ワークフロー起動
    CI->>CI: 6. テスト実行
    CI->>CI: 7. カバレッジ計測
    CI->>Git: 8. ステータス報告
    
    alt テスト成功
        Git->>Review: 9. レビュー依頼可能
        Review->>Git: 10. レビュー承認
        Git->>Git: 11. マージ
    else テスト失敗
        Git->>Dev: 9. 失敗通知
        Dev->>Local: 修正・再プッシュ
    end

ローカルでのプレコミットフック

CIに送る前にローカルでテストを実行するため、huskyを使用したGit Hooksを設定します。

1
2
3
4
5
{
  "scripts": {
    "prepare": "husky"
  }
}

.husky/pre-commitファイルを作成します。

1
2
npm run lint
npm test

.husky/pre-pushファイルを作成します。

1
npm run test:coverage

これにより、CIでの失敗を事前に防ぎ、フィードバックサイクルを短縮できます。

Flaky Test(不安定なテスト)への対処

CI環境でのみ失敗するテストや、不定期に失敗するテストへの対処法を解説します。

リトライ機構の実装

1
2
3
4
5
6
- name: Run tests with retry
  uses: nick-fields/retry@v3
  with:
    timeout_minutes: 10
    max_attempts: 3
    command: npm test

Flaky Testの検出と管理

1
2
3
4
5
6
7
- name: Run tests with Flaky detection
  run: |
    for i in {1..5}; do
      npm test && break
      echo "Attempt $i failed, retrying..."
      sleep 5
    done

Flaky Testを防ぐためのベストプラクティス

原因 対策
時間依存 テスト用の固定時刻を注入
外部API依存 モック・スタブを使用
実行順序依存 テストの独立性を確保
非同期処理 適切なawait・タイムアウト設定
リソース競合 テストごとにリソースを分離

パフォーマンス最適化

キャッシュの活用

依存関係のキャッシュを活用してビルド時間を短縮します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20.x'
    cache: 'npm'

- name: Set up JDK
  uses: actions/setup-java@v4
  with:
    java-version: '21'
    distribution: 'temurin'
    cache: maven

並列実行

テストを並列実行してCI時間を短縮します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        shard: [1, 2, 3, 4]
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
          cache: 'npm'
      - run: npm ci
      - run: npm test -- --shard=${{ matrix.shard }}/4

変更されたファイルのみテスト

プルリクエストで変更されたファイルに関連するテストのみを実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
- name: Get changed files
  id: changed-files
  uses: tj-actions/changed-files@v44
  with:
    files: |
      src/**/*.ts
      src/**/*.tsx

- name: Run related tests
  if: steps.changed-files.outputs.any_changed == 'true'
  run: npm test -- --findRelatedTests ${{ steps.changed-files.outputs.all_changed_files }}

まとめ

TDDとCI/CDパイプラインの連携は、個人の開発習慣をチーム全体の品質保証の仕組みへと昇華させます。本記事で解説した内容を実践することで、以下の効果が期待できます。

導入前 導入後
テスト実行は開発者任せ すべてのコミットで自動テスト
バグは本番で発覚 プルリクエスト時点で検知
カバレッジは不明 常に可視化・閾値で品質担保
レビューでテスト確認 テスト通過が前提
リファクタリングに不安 自信を持って改善

GitHub Actionsを活用したCI/CDパイプラインは、TDDの効果を最大化し、チーム全体で継続的に高品質なソフトウェアを提供するための基盤となります。まずは基本的なテスト自動化から始め、段階的にカバレッジ計測、通知設定、ブランチ保護を導入していくことをおすすめします。

参考リンク