GitHub Actions x Python テスト自動化完全ガイド|CI/CD構築から品質管理まで

GitHub Actions と pytest を組み合わせると、コードをプッシュするたびに自動でテストが走る CI 環境を無料で構築できます。

本記事では、ゼロから始める workflow ファイルの書き方から、matrix build・pip キャッシュ・artifact 保存まで、実務で使える設定をまとめました。

1. GitHub Actions で Python テストを自動化するメリット

CI/CD パイプラインを整備すると、以下の効果が得られます。

  • プッシュ・PR のたびにテストが自動実行され、バグの混入を早期に検知できる
  • 複数の Python バージョンへの対応状況を並行確認できる
  • テスト結果がリポジトリ上に保存され、チームで共有できる
  • GitHub Actions はパブリックリポジトリであれば無料で利用できる

2. ディレクトリ構成と pytest 基本設定

本記事で想定するディレクトリ構成は以下のとおりです。

my_project/
├── .github/
│   └── workflows/
│       └── test.yml
├── src/
│   └── calculator.py
├── tests/
│   ├── __init__.py
│   └── test_calculator.py
├── pyproject.toml
└── requirements.txt

pytest の設定(pyproject.toml)

pytest はプロジェクトルートの pyproject.toml で設定を管理するのが現在の推奨方法です(公式ドキュメントより)。

[tool.pytest.ini_options]
minversion = "6.0"
addopts = ["-ra", "-q", "--strict-markers"]
testpaths = ["tests"]
  • addopts = ["-ra", "-q"]:失敗・スキップしたテストの要約を表示し、出力を簡潔にする
  • testpaths = ["tests"]:テストを探索するディレクトリを明示する
  • --strict-markers:未定義のマーカー使用時にエラーにする

サンプルの実装コードとテストコード

テスト対象(src/calculator.py):

def add(a: float, b: float) -> float:
    return a + b

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

テストコード(tests/test_calculator.py):

import pytest
from src.calculator import add, divide

def test_add():
    assert add(1, 2) == 3
    assert add(-1, 1) == 0

def test_divide():
    assert divide(10, 2) == 5.0

def test_divide_by_zero():
    with pytest.raises(ValueError, match="Cannot divide by zero"):
        divide(1, 0)

3. 基本的な workflow ファイル(.github/workflows/test.yml)

まず最小構成の workflow から始めます。

# .github/workflows/test.yml
name: Python Tests

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

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pytest pytest-cov

      - name: Run tests
        run: pytest

このファイルを .github/workflows/test.yml として配置すると、maindevelop ブランチへのプッシュ時および main への PR 作成時にテストが実行されます。

4. pip キャッシュで依存関係インストールを高速化

GitHub Actions は実行のたびにクリーンな環境を用意するため、pip install が毎回走ります。actions/setup-pythoncache オプションを使うと、requirements.txt に変更がない限りキャッシュが再利用されます。

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
          cache: 'pip'

GitHub 公式ドキュメントでは、このシンプルな設定が推奨されています。requirements.txt の内容をハッシュ化してキャッシュキーが自動生成されるため、依存関係が変わったときは自動的にキャッシュが更新されます。

キャッシュの詳細設定については、以下の記事も参考にしてください。

GitHub Actions キャッシュ完全ガイド2026【npm・pip高速化・実装一覧・失敗しない設定】

5. matrix build で複数 Python バージョンをテスト

ライブラリを公開する場合や、チームで異なる Python バージョンを使っている場合は、matrix build で複数バージョンを並行テストできます。

    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12"]
      fail-fast: false

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'

fail-fast: false を設定することで、Python 3.9 でテストが失敗しても Python 3.12 のテスト結果も取得できます。特定の組み合わせを除外したい場合は exclude を使います。

6. テスト結果を artifact として保存する

テスト結果を JUnit XML 形式で出力し、artifact として保存すると後から確認・ダウンロードできます。

      - name: Run tests with coverage
        run: |
          pytest \
            --junitxml=junit/test-results.xml \
            --cov=src \
            --cov-report=xml:coverage.xml

      - name: Upload test results
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results
          path: junit/test-results.xml
        if: ${{ always() }}

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage.xml
        if: ${{ always() }}

if: ${{ always() }} は GitHub Actions 公式ドキュメントで推奨されている設定です。テストが失敗した場合でも artifact が保存されるため、失敗の原因調査に使えます。

7. 完成版 workflow ファイル全文

# .github/workflows/test.yml
name: Python Tests

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

jobs:
  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12"]
      fail-fast: false

    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install pytest pytest-cov

      - name: Run tests with coverage
        run: |
          pytest \
            --junitxml=junit/test-results-${{ matrix.python-version }}.xml \
            --cov=src \
            --cov-report=xml:coverage-${{ matrix.python-version }}.xml

      - name: Upload test results
        uses: actions/upload-artifact@v4
        with:
          name: pytest-results-${{ matrix.python-version }}
          path: junit/test-results-${{ matrix.python-version }}.xml
        if: ${{ always() }}

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ matrix.python-version }}
          path: coverage-${{ matrix.python-version }}.xml
        if: ${{ always() }}

8. よくあるエラーと対処法

ModuleNotFoundError: No module named ‘src’

テスト実行時に src パッケージが見つからないエラーが出る場合は、PYTHONPATH を設定します。

      - name: Run tests
        run: pytest
        env:
          PYTHONPATH: ${{ github.workspace }}

キャッシュが効かない

requirements.txt が存在しない場合、cache: 'pip' オプションはキャッシュキーを生成できず警告が出ます。requirements.txt を用意するか、pyproject.toml に依存関係を記載してください。

matrix build の一部が常に失敗する

特定バージョンのみ失敗する場合は exclude で一時的に除外して原因を調査します。fail-fast: false にしていれば他バージョンの結果は取得できます。

関連記事

まとめ

  • 基本構成:checkout → setup-python → pip install → pytest
  • pip キャッシュsetup-pythoncache: 'pip' で自動設定
  • matrix buildstrategy.matrix.python-version で複数バージョンを並行テスト
  • artifact 保存upload-artifact@v4if: always() で失敗時も結果を保存

まずは完成版 workflow ファイルをそのままコピーして、自分のリポジトリに配置してみてください。

🤖 このブログはAIで自動運営しています。 同じ仕組みを御社にも導入できます。 無料相談はこちら
タイトルとURLをコピーしました