Python | テスト・設計・品質:CI(GitHub Actions)

Python Python
スポンサーリンク

CI(GitHub Actions)って何?一言でいうと「プッシュした瞬間に“自動でテストしてくれるロボット”」

CI(Continuous Integration)は、「コードを少し変更するたびに、自動でテストやチェックを回して、壊れていないかを常に確認し続ける仕組み」です。
GitHub Actions は、その CI を GitHub 上で簡単に動かせる“自動実行ロボット”だと思ってください。

あなたが git push した瞬間に、

テストを実行する
型チェック(mypy)を回す
Lint(ruff, flake8 など)を回す

といったことを、GitHub が勝手にやってくれます。
「人間がうっかり壊したコードを main に混ぜない」ための安全装置です。


まずは「最小のPython用CI」をイメージで掴む

どんなことが自動で行われるのか

例えば、こんな Python プロジェクトを想像してください。

src/ にアプリ本体
tests/ にテスト

普段はローカルでこうやってテストを回しているとします。

pytest

これを「GitHub に push したときにも自動でやってほしい」のが CI です。

GitHub Actions では、リポジトリの中に

.github/
  workflows/
    ci.yml

というファイルを置いて、「こういう手順でテストしてね」と書きます。


GitHub Actions のワークフローを具体的に見てみる

一番シンプルな Python CI の例

例えば、こんな ci.yml を置きます。

name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest

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

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

      - name: Install dependencies
        run: |
          pip install -U pip
          pip install -e .[dev]

      - name: Run tests
        run: |
          pytest
YAML

これを噛み砕いて説明します。

on:
いつ動かすか。ここでは main ブランチへの push と、main に向けた Pull Request のとき。

jobs:
実行する「仕事」の定義。ここでは test というジョブが一つ。

runs-on:
どんな環境で動かすか。ubuntu-latest は「GitHub が用意してくれる Ubuntu マシン」。

steps:
そのジョブの中で、何を順番にやるか。

actions/checkout でリポジトリのコードを取ってくる
actions/setup-python で Python のバージョンを指定する
pip install で依存関係を入れる
pytest を実行する

これだけで、「push したら自動で pytest が走る」状態になります。


重要ポイント1:CIは「ローカルでやっていることを、他人のマシンで再現する」もの

ローカルでできていないことは、CIでもできない

CI を考えるときの大事な視点は、

「ローカルで手動でやっている一連の作業を、そのまま YAML に書く」

ということです。

普段あなたが、

仮想環境を作る
依存をインストールする
テストを実行する

という流れで開発しているなら、
それをそのまま GitHub Actions の steps に落とし込めばいい。

逆に言うと、

ローカルでテストがまともに回っていない状態で CI を入れても、
ただ「失敗する自動化」ができるだけです。

だから順番としては、

ローカルで pytestmypy を安定して回せるようにする
そのコマンドを GitHub Actions に書く

という流れが自然です。


重要ポイント2:テスト・型チェック・Lint を「分けて」考える

1つのCIで複数のチェックを回す

Python の品質チェックは、だいたい次の3つに分かれます。

テスト(pytest)
型チェック(mypy)
Lint(ruff, flake8 など)

これらを CI で全部回すのが理想です。

例えば、こういう構成にできます。

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      ...  # さっきと同じ
      - name: Run tests
        run: pytest

  lint:
    runs-on: ubuntu-latest
    steps:
      ...
      - name: Run ruff
        run: ruff check .

  typecheck:
    runs-on: ubuntu-latest
    steps:
      ...
      - name: Run mypy
        run: mypy src tests
YAML

ポイントは、「ジョブを分けると、どこで落ちたかが一目で分かる」ことです。

テストが落ちたのか
型チェックが落ちたのか
Lint が落ちたのか

これが分かるだけで、修正のストレスがかなり減ります。


重要ポイント3:Pull Request と CI をセットで考える

「CIが通らないとマージできない」状態を作る

GitHub では、ブランチ保護ルールを使って、

「CI が成功していない Pull Request は main にマージできない」

という設定ができます。

これを有効にすると、

テストが落ちているコード
型チェックが通っていないコード
Lint がボロボロなコード

が main に混ざるのを防げます。

つまり、CI は「レビュー前の最低ラインチェック」を自動でやってくれる存在になります。

人間のレビューは「設計・命名・仕様」などに集中できるようになり、
テスト落ち・フォーマット崩れのような機械的な指摘は CI に任せられます。


重要ポイント4:フォルダ構成標準化とCIはセットで効いてくる

src/ と tests/ を前提にしたCIを書く

フォルダ構成を標準化しておくと、CI の設定もテンプレート化しやすくなります。

例えば、

ソースコードは src/
テストは tests/

という前提なら、mypy や pytest のコマンドはだいたいこうなります。

pytest
mypy src tests
ruff check src tests

これを GitHub Actions にそのまま書けばいい。

プロジェクトごとに「どこにコードがあるか」を考え直さなくて済むので、
CI の導入コストが一気に下がります。


初心者が「最初のCI」を入れるときのステップ

ステップ1:ローカルで pytest を安定して回せるようにする

まずは、ローカルで

pytest

が普通に通る状態にしておきます。
ここがぐらついていると、CI も当然ぐらつきます。

ステップ2:最小の ci.yml を置いてみる

さきほどの「テストだけ回す」CI を、そのまま .github/workflows/ci.yml に置いてみます。

push して、GitHub の「Actions」タブを開くと、
実際に CI が動いている様子が見られます。

ここで一度、「自分のテストが他人のマシンでも再現できている」感覚を掴むのが大事です。

ステップ3:慣れてきたら mypy や ruff を足していく

テストが安定してきたら、
次は型チェック(mypy)や Lint(ruff)を CI に追加していきます。

いきなり全部を完璧にしようとしなくてよくて、

まずはテストだけ
次に Lint
最後に型チェック

のように、少しずつ育てていくイメージで十分です。


まとめ(CI × GitHub Actions は「未来の自分を守る自動テスト係」)

初心者目線で整理すると、CI(GitHub Actions)はこういう存在です。

GitHub に push / PR したタイミングで、自動でテスト・型チェック・Lint を回して、「壊れたコードが main に混ざる」のを防ぐ仕組み。
やっていることの本質は「ローカルで手動でやっている一連のコマンドを、YAML に書いて他人のマシン(GitHub)で再現している」だけで、難しい魔法ではない。
フォルダ構成(src / tests)やツール(pytest / mypy / ruff)を標準化しておくと、CI の設定もテンプレート化できて、新しいプロジェクトでもすぐに「自動テスト係」を雇えるようになる。

タイトルとURLをコピーしました