CIテスト設計・コード作成ガイド

push で安定して回るCIを作るために、テストケースの粒度と実装手順をテンプレート化した資料です。

CI/CDの利点と欠点

観点利点欠点・注意点
品質 pushごとに自動テストされるため、バグの混入を早く検知できる。 テストが薄いと「CIが緑でも本番で壊れる」状態になる。テスト設計が前提。
速度 手動手順を削減でき、リリース頻度を上げやすい。 ジョブが肥大化すると待ち時間が増え、開発速度が逆に低下する。
運用 デプロイ手順をコード化でき、属人化を防ぎやすい。 環境差分(dev/stg/prod)管理を誤ると、事故が自動で拡大する。
監査 誰が何をいつ反映したかを履歴で追跡できる。 シークレット管理が甘いと、ログや設定から漏えいリスクが生じる。

実装例(現場で使う最小セット)

  1. GitHub Actions + Docker + ECR + CodeDeploy(Blue/Green) を基本形にする。
  2. CIは pytest / ruff / 依存脆弱性チェックを並列で実行する。
  3. CDは「main直pushで自動」ではなく、承認ステップを挟む。
  4. 本番切替はBlue/Greenにし、切替後もBlueを短時間保持して即時ロールバック可能にする。
# buildspec.yml (CodeBuild 例)
version: 0.2
phases:
  install:
    commands:
      - pip install -r requirements.txt -r requirements-dev.txt
  pre_build:
    commands:
      - pytest -q
      - ruff check .
  build:
    commands:
      - docker build -t app:$CODEBUILD_RESOLVED_SOURCE_VERSION .
      - docker tag app:$CODEBUILD_RESOLVED_SOURCE_VERSION $ECR_REPO_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION
      - docker push $ECR_REPO_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION
artifacts:
  files:
    - appspec.yml
    - taskdef.json
# GitHub Actions でPR時のみCI、mainマージ後にCDを起動する例
name: ci-cd
on:
  pull_request:
    branches: [main]
  push:
    branches: [main]

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.12" }
      - run: pip install -r requirements.txt -r requirements-dev.txt
      - run: pytest -q
      - run: ruff check .

  cd:
    if: github.event_name == 'push'
    needs: [ci]
    runs-on: ubuntu-latest
    steps:
      - run: echo "CodePipeline trigger"

テストレイヤー設計

Unit Test

目的: 関数/クラス単位の仕様確認

到達点: 最速で壊れた箇所を検知

Integration Test

目的: DB/API/外部連携を含む接続確認

到達点: 契約不一致の早期発見

E2E Test

目的: 画面/APIを通した業務フロー確認

到達点: 本番に近い振る舞い保証

CIで管理する推奨ファイル

ファイル用途
tests/test_<feature>.py機能単位のテストケースを集約
conftest.py共通fixture・テストデータセットアップ
.github/workflows/ci.ymlpush/PR時の実行定義
requirements-dev.txtpytest, ruff, mypy などCI依存

テストケース作成手順(実務向け)

  1. ユーザーストーリーを「正常系」「異常系」「境界値」に分割する。
  2. 入力、期待結果、失敗時メッセージを1ケースごとに明文化する。
  3. 1ケース1アサーションを基本にして原因切り分けを容易にする。
  4. 外部依存(API/DB)はfixtureやmockで制御し、テストを再現可能にする。
  5. 失敗時のログ(request id、payload要約)をCIから辿れるようにする。

pytest サンプル(ユニット + 統合)

import pytest

def test_score_returns_100_when_all_checks_pass():
    result = calc_score(required=10, passed=10, warnings=0)
    assert result == 100

@pytest.mark.django_db
def test_pipeline_event_is_saved(client):
    payload = {"event": "push", "ref": "main"}
    res = client.post("/api/pipeline/events/", data=payload)
    assert res.status_code == 201
    assert res.json()["ok"] is True

GitHub Actions CI サンプル

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install -r requirements.txt -r requirements-dev.txt
      - run: pytest -q
      - run: ruff check .

運用メモ