1. はじめに
GitHub Actionsを用いて自動テストの実行と結果の集計を行う方法を説明します。
具体的には、ソースコードがGitHubへpush
されたタイミングで、pythonで書かれたテストをpytest
を使って実行し、GitHub上に下図のサマリを表示します。
今回、GitHub Actionsを初めて使ったので、学習のためにGitHub Actionsの基本についても触れています。
2. GitHub Actionsとは
GitHubのCI/CDツール。
push, pull requestなどのGiHub上のアクティビティやスケジュールした時間、外部イベントをトリガーとして、ワークフローを作成できます。
特徴
- Linux, macOS, Windowsすべてのコンテナに対応
- Node.js, Python, Java, Rubyなど、様々な言語に対応
- 複数のジョブを並行してビルド可能
用語
-
ワークフロー
- トリガーと1つ以上のジョブで構成される手順
- リポジトリ内の
.github/workflows
ディレクトリにYAML形式で定義
-
イベント
- ワークフローをトリガーする特定のアクティビティ
- 外部イベントをトリガーしたい場合は、webhookを使用する
-
ジョブ
- 一連のステップ
- 1つのワークフローに複数のジョブが存在する場合は、並行して実行される
-
ステップ
- ジョブでコマンドを実行できる個々のタスク
- アクションまたはシェルコマンドで設定できる
-
アクション
- ワークフローを作成するための最小単位のコマンド
- 独自のアクションを作成することも、オープンソースのアクションを使用することも可能
-
成果物(Artifacts)
- ワークフロー実行中に生成されるファイル
ワークフローの構造
GitHub Docsにあるサンプルを用いて各項目を説明します。それぞれの詳細は、GitHub Actionsのワークフロー構文が詳しいです。
name: learn-github-actions
on: [push]
jobs:
check-bats-version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- run: npm install -g bats
- run: bats -v
項目名 | 説明 |
---|---|
name: |
ワークフローの名前。GitHubのActionsに表示される。なんでもよい |
on: |
必須。トリガーのイベント。上の例ではpush する度にジョブが実行される |
jobs: |
ワークフローで実行されるジョブのグループ。複数のジョブの集合 |
check-bats-version: |
ジョブのID。英字または_ で始める必要があり、英数字と- 、_ しか使用できない |
runs-on |
必須。ジョブが実行されるマシンの種類。OSバージョンを指定することもできる |
steps: |
ジョブで実行されるステップのグループ |
- uses: |
使用するアクション |
- run: |
実行するシェルコマンド |
以下の階層をイメージするとよいと思います。
name: ワークフローの名前
on: トリガーのイベント
jobs:
ジョブのID
runs-on: ジョブが実行されるマシンの種類
steps:
- uses: アクション
- run: シェルコマンド
ワークフローの作り方
ワークフロー(YAMLファイル)を作成するには大きく2つの方法があります。完成するファイルは同じなので、使いやすい方でOKです。
方法①テキストエディタ
VSCodeなどのテキストエディタでリポジトリ内の.github/workflows
ディレクトリにYAMLファイルを作成する方法。VSCodeの場合、GitHub Actionsという拡張機能を使うとよい。
方法②GitHub
GitHubのActionsタブ>「New workflow」>「set up a workflow yourself」で新規作成。テンプレートに沿って作成することができ、下図のMarketplaceからアクションを検索しやすい。
3. 自動テストの実行と結果の集計
今回は、自動テスト(pytest)の実行と結果の集計を行うワークフロー(YAMLファイル)を作成します。
大まかな流れ
- トリガーの指定
- ジョブの条件
- python環境のセットアップ
- 自動テストを実行する
- 自動テストの結果をGitHub上に表示する
- ワークフローの実行と確認
先に、YAMLファイルの完成版は以下の通り。各トリガーやステップの詳細は後述します。
name: Run pytest
on: [push]
jobs:
pytest:
runs-on: ubuntu-latest
steps:
# リポジトリをチェックアウト
- name: Checkout
uses: actions/checkout@v2
# Pythonの環境をセットアップ
- name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.7
# pytestをインストール
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
# pytest -> JUnit xml形式で結果を出力
- name: PyTest
run: |
python -m pytest test --junit-xml results/pytest.xml
continue-on-error: true
# テスト結果の表示
- name: Upload Unit Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: Unit Test Results (Python 3.7)
path: results/*.xml
- name: Download Artifacts
if: success() || failure()
uses: actions/download-artifact@v2
with:
path: artifacts
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
junit_files: artifacts/**/*.xml
※2022/10/21 files
だとWarningが出たのでjunit_files
に修正。
トリガーの指定
push
したタイミングで自動テストを実行したいので、push
を指定します。
on: [push]
他にも、
-
Pull Request
のタイミング -
Pull Request Review
のタイミング - ブランチの指定
- タグの指定
などをトリガーにできます。その他のイベントはワークフローをトリガーするイベントを参照。
# 例:mainブランチでpushもしくはPull Requestが行われたタイミングで実行
on:
push:
branches:
- main
pull_request:
branches:
- main
ジョブの条件
ジョブ(pytest)の実行マシンを指定します。今回は、ubuntu-latest
を使用します。macOS
、Windows
も使用でき、その場合は「Publish Unit Test Results」のアクションの書き方が異なります。
jobs:
pytest:
runs-on: ubuntu-latest
python環境のセットアップ
ここからはステップの記述です。
自動テストを実行するためにpython環境をセットアップします。今回はpython-version: 3.7
を使っています。
使用するアクション
-
Checkout
- ワークフローがリポジトリにアクセスできるように、リポジトリをチェックアウトする
-
Setup Python
- python環境をセットアップする
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python 3.7
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install dependencies
run: | # コマンドを複数行記述したい場合は「|」を使う
python -m pip install --upgrade pip
pip install pytest
自動テストを実行する
自動テストの実行とJUnit XML形式の結果の出力をコマンドで記述します。また、ステップが失敗しても次のジョブへ進むために、continue-on-error: true
を設定します。
- name: PyTest
run: |
python -m pytest test --junit-xml results/pytest.xml
continue-on-error: true
自動テストの結果をGitHub上に表示する
自動テストの結果をGitHubのArtifacts上にアップロードし、結果を表示します。
使用するアクション
-
Upload a Build Artifact
- Artifactをアップロードしジョブ間でデータを共有する
- Download artifact
-
Publish Unit Test Results
- テスト結果を分析し結果をGitHub上で表示する。JUnit XML形式に対応しており、Linux、macOS、Windows上で動作する
# テスト結果の表示
- name: Upload Unit Test Results
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name: Unit Test Results (Python 3.7)
path: results/*.xml
- name: Download Artifacts
if: success() || failure()
uses: actions/download-artifact@v2
with:
path: artifacts
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
junit_files: artifacts/**/*.xml
ArtifactsはActionsタブの以下から取得することができます。
ワークフローの実行と確認
何かしら変更を加えてpush
すると、ワークフローが実行されます。
実行したワークフローは、GitHubのActionsタブまたはcommit履歴から閲覧できます。
▼GitHubのActionsタブ(コミットメッセージは適当です)
ワークフローを選択すると、実行結果のサマリを確認することができます。
4. Tips: Selenium + pytestを実行する
Seleniumを使った自動テストを実行したい場合も前述のワークフローを流用できますが、2つポイントがあります。
-
webdriver_manager
を用いて、ブラウザバージョンに対応するdriverを自動で適用する - Chromeを
"--headless"
モードで実行する
まず、Install dependencies
のステップに以下のコマンドを追加し、webdriver-managerをインストールします。
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
pip install webdriver-manager
テストスクリプト内での使い方は次の通り。
# Selenium 4
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager
options = Options()
options.headless = True
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
# Selenium 3の場合
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
driver = webdriver.Chrome(ChromeDriverManager().install(), chrome_options=options)
※"--headless"
をoptionに設定せずにGitHub Actionsを実行すると、次のエラーメッセージが表示されてしまいます。
selenium.common.exceptions.WebDriverException: Message: unknown error: Chrome failed to start: exited abnormally.
あとは通常通りGitHub Actionsを実行すればOKです。
5. 所感
サードパーティ含むアクションのライブラリが充実していて、思ったよりもゴリゴリコマンドを書く必要がない印象を受けました。また、GitHub Actionsに関するドキュメントが整備されていたのがありがたかったです。
CI/CDツールといえばJenkinsやCircleCIのイメージが強かったのですが、GitHubでソースコード管理をしている場合は十分候補になりそうです。