自作UPM (Unity Package Manager)パッケージをGitHub Actions上でテストするためのワークフローが確立できたので紹介します。
前提とするのは、リポジトリのルートがパッケージのルートディレクトリである(package.jsonがある)構成です。 Unityプロジェクトの一部をUPMパッケージとして公開している構成でも、パスを書き換えるなどすることで応用できるはずです*1。
また、テストアセンブリの名前末尾が .Tests
であることを前提としています*2。
実現していることは次のとおりです。
- 先行ジョブのキャンセル
- 複数Unityバージョンでのテスト実行(互換性)
- テスト実行用Unityプロジェクトの生成
- テスト実行のための依存関係の解決
- テスト実行
- コードカバレッジの集計
- Slack通知
以下、処理順に説明します。 記事の最後に、実際に動作しているリポジトリのURLを紹介しています。
ワークフロー解説
前提として、このワークフローは、Pull Requestへのpushおよび、masterブランチへのマージで実行されます。ただしMarkdownファイルおよび test.yml
以外のワークフローの更新ではトリガしないようにしています。
on: push: branches: - master paths-ignore: - '**.md' - '.github/' - '!.github/workflows/test.yml' pull_request: types: [ opened, synchronize, reopened ] # Same as default paths-ignore: - '**.md' - '.github/' - '!.github/workflows/test.yml'
先行ジョブのキャンセル
同一ブランチでワークフロー動作中に新たなpushがあったとき、先行しているジョブをキャンセルするよう設定しています。
concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true
複数Unityバージョンでの実行設定
matrix
にテストを実行するUnityバージョンを設定します。
include
は、コードカバレッジの集計を実行するバージョン1つ(下例ではUnity 2022.2.5f1)にのみoctocov
フラグを立てています。
jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: unityVersion: - 2019.4.40f1 - 2020.3.42f1 - 2021.3.15f1 - 2022.2.5f1 include: - unityVersion: 2022.2.5f1 octocov: true
以降のステップは、Unityバージョンごとに実行されます。
チェックアウト
サブモジュールがある場合はsubmodules
にtrue
やrecursive
を指定します。
Git LFSにテスト実行に必要なファイルが存在する場合はlfs
も設定します。
- name: Checkout repository uses: actions/checkout@v3 with: submodules: false lfs: false
テスト実行用Unityプロジェクトの生成
テスト実行に使用する空のUnityプロジェクトをUnityProject~
ディレクトリに生成します*3。
末尾に~
をつけることで、Unityの管理対象にならないようにしています。
- name: Crete project for tests uses: nowsprinting/create-unity-project-action@v2 with: project-path: UnityProject~
空プロジェクトの生成には自作のActionを使用しています。Unityライセンス不要*4で、Unity 2018.1.0f1*5プロジェクトを生成するものです。
テスト実行のための依存関係の解決
最低限、リポジトリ直下のディレクトリをPackages/manifest.jsonのdependencies
に追加する必要がありますし、UniTaskなどサードパーティのパッケージに依存している場合はScoped Registriesの設定も必要です。
また、Unity Test FrameworkパッケージのアップデートおよびCode Coverageパッケージもインストールしておきます*6。
manifest.jsonの更新には、openupm-cliを使用しています。
- name: Set package name run: | echo "package_name=$(grep -o -E '"name": "(.+)"' ./package.json | cut -d ' ' -f2)" >> "$GITHUB_ENV" - name: Install dependencies run: | npm install -g openupm-cli openupm add -f com.unity.test-framework@1.3.2 openupm add -f com.unity.testtools.codecoverage@1.2.2 openupm add -f com.cysharp.unitask@2.3.3 openupm add -ft "${{ env.package_name }}"@file:../../ working-directory: ${{ env.CREATED_PROJECT_PATH }}
openupm add
の-f
オプションは、インストールするパッケージのバージョン制限を無視してくれます。
この時点でUnityプロジェクトは2018なので、前提条件を満たさないパッケージもあるためです。
実際に使用するUnityバージョンはテスト実行時に指定し、そこでアップデートが走ります。
また-t
オプションは、manifest.jsonのtestables
に追加し、パッケージをテスト実行対象にしてくれるものです。
[2024/04/29追記] CIで使用しているLinux editorにはいくつかバグがあり、対象パッケージをローカルパッケージとしてインストールする方式では問題が出ることがありました*7*8。 現在は、直接Packages下にチェックアウトして埋め込みパッケージ(embedded package)扱いにすることで問題を回避できています。
テスト実行
テスト実行前に、Code Coverageパッケージのフィルタを組み立てます。 フィルタを適切に設定しないと開発対象外のコードまで含まれてしまいテスト実行に時間がかかりますし、カバレッジの数値にも影響します。 またテストコードは常にカバレッジ100%になるため、これも除くべきです。
次のように、リポジトリに含まれるasmdefファイルすべて(末尾.Tests
は除く)と、Assets下を対象にしています*9。
- name: Set coverage assembly filters run: | assemblies=$(find . -name "*.asmdef" -maxdepth 3 | sed -e s/.*\\//\+/ | sed -e s/\\.asmdef// | sed -e s/^.*\\.Tests//) echo "assembly_filters=$(echo ${assemblies[*]} | sed -e s/\ /,/g),+<assets>,-*.Tests" >> "$GITHUB_ENV"
テスト実行にはgame-ci/unity-test-runnerを使用しています。ポイントは、
unityVersion
にmatrixのUnityバージョンを指定projectPath
に生成したUnityプロジェクトのパスを設定customParameters
にはGitHub Actiuons上で実行したくないテストをスキップする指定
coverageOptions
の設定内容については
Using Code Coverage in batchmode | Code Coverage | 1.2.5
を参照してください。
- name: Run tests uses: game-ci/unity-test-runner@v2 with: githubToken: ${{ secrets.GITHUB_TOKEN }} unityVersion: ${{ matrix.unityVersion }} checkName: test result (${{ matrix.unityVersion }}) projectPath: ${{ env.CREATED_PROJECT_PATH }} customParameters: -testCategory "!IgnoreCI" coverageOptions: generateAdditionalMetrics;generateTestReferences;generateHtmlReport;generateAdditionalReports;dontClear;assemblyFilters:${{ env.assembly_filters }} UNITY_LICENSE: ${{ secrets[env.secret_key] }} id: test
コードカバレッジの集計
Code Coverageパッケージv1.2からカバレッジ情報をLCOV形式でも出力してくれるようになったため、octocovによる集計が可能になりました。 カバレッジやcode:test比の増減をPull Requestコメントやジョブサマリに貼ってくれたりするので便利です。
事前に.octocovファイルにあるカバレッジレポートのパスをgame-ci/unity-test-runnerの出力パスに書き換えてから実行しています。
- name: Set coverage path for octocov run: sed -i -r 's/\.\/Logs/${{ steps.test.outputs.coveragePath }}/' .octocov.yml if: ${{ matrix.octocov }} - name: Run octocov uses: k1LoW/octocov-action@v0 if: ${{ matrix.octocov }}
このステップはレポートの重複を避けるため、matrix
に設定したoctocov
フラグの付けられたバージョンでのみ実行しています。
Slack通知
matrixで実行した全テストの終了を待ち合わせてSlackで通知しています。
待ち合わせはneeds
で、またテスト失敗も拾うためにif: always()
を追加しています。
通知にはGamesight/slack-workflow-statusを使用しています*10。
notify: needs: test runs-on: ubuntu-latest if: always() steps: - uses: Gamesight/slack-workflow-status@v1.2.0 with: repo_token: ${{ secrets.GITHUB_TOKEN }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
このワークフローが完走したときのジョブは次のようになります。
リポジトリ紹介
紹介したワークフローは、次のリポジトリで実際に使用しています*11。ワークフローはどれもほぼ同じものを使い回せています(依存パッケージの違い程度)。
blender-like-sceneview-hotkeys
Blender 的なテンキー操作でSceneViewの視点を操作できるエディタ拡張
紹介記事
create-script-folders-with-tests
C#スクリプトを置くRuntimeとEditor、およびそれぞれのTestsフォルダとasmdefを生成してreferencesの設定まで行なうエディタ拡張*12
test-helper.monkey
uGUIを対象としたモンキーテストのリファレンス実装とヘルパーライブラリ(いまのところ)
紹介したAction・ツールまとめ
宣伝
テストの書きかたについてはこちらを参照
*1:作業用Unityプロジェクトをセットでリポジトリに入れていればそのままテスト実行できるのですが、互換性テストではUnityバージョンをダウングレードできないため、プロジェクトのUnityバージョンを上げずにキープするか、作業用とは別にテスト実行時用のUnityプロジェクトを用意する必要があります。後者の場合、この記事が応用できるはずです
*2:正確にはasmdefファイル名で判断しています。本来であればファイルの中身を見るべきなのですが一致している前提でサボっています
*3:都度生成するので、リポジトリに含まないよう.gitignoreに追加してあります
*4:プロジェクトのテンプレートをコピーしているだけなので
*5:UPMサポートが入ったバージョン
*6:例では依存パッケージのバージョンを指定していますが、省略して常に最新バージョンを使用することもできます
*7:https://github.com/nowsprinting/create-script-folders-with-tests/issues/17
*8:https://github.com/nowsprinting/test-helper/pull/71
*9:通常Assetsは空ですが、Samplesに含まれるテストを実行するためにコピーするケースを想定しています
*10:game-ci/unity-test-runnerと若干相性が悪く、skipされるテストがあると失敗扱いで通知されてしまいます。落ち着いたらどちらかを改善できればと思いつつ…
*11:Unity 2020と2021は実行を省略しています
*12:Unity 2020以降のLinux editorでテストが失敗していますが利用はできます。Unityにバグレポ済み