はじめに
こちらはUnity2024 Advent Calendarの12/20
記事になります。是非他の方の記事もチェックしてみてください。
qiita.com
今回はUnityでのコードメトリクスを可視化するための分析基盤の作り方について紹介したいと思います。
Code Coverageとは
Code Coverageとはテストで用いられるコードがどれだけ実行されたかを示す指標です。テストケースがあまり網羅されていないコードを検出することに役に立ちます。
コード網羅率(コードもうらりつ、英: Code coverage、コードカバレッジ)は、ソフトウェアテストで用いられる尺度の1つである。プログラムのソースコードがテストされた割合を意味する。この場合のテストはコードを見ながら行うもので、ホワイトボックステストに分類される。
コード網羅率は体系的なソフトウェアテストのための技法として最初に生み出されたものの1つである。1963年の Communications of the ACM にある Miller と Maloney の論文に言及されているのが最初である。
構成概要
具体的には以下の構成で構築しました。
主に以下を利用しています。
CodeCoverage Package
coverlet.collector
octocov
BigQuery
Looker Studio
それぞれについては後述します。
Unity公式パッケージ「CodeCoverage」
Unityの公式パッケージであるCode Coverage Package
を利用することで、コードカバレッジを取得することができます。
docs.unity3d.com
またUnityに依存していないコードについてはdotnet test
による計測ができないかは一考の余地があると思います。Unityに依存するコードは面倒くさいことが多くイテレーションも遅いので、なるべくUnityに依存するコードとUnityに依存しないコードをアセンブリレベルで分離したほうが良いでしょう。
dotnet test
を用いたコードカバレッジの収集方法は下記に記載しています。
www.hanachiru-blog.com
セットアップ方法
Package Managerからインストールします。Add Package from git URL...
からcom.unity.testtools.codecoverage
と入力すればOKです。
実行方法
コードカバレッジはCIで収集するケースがほとんどだと思うので、Unityのバッチモードでテストを実行 + カバレッジ収集
を行います。
Using Code Coverage in batchmode | Code Coverage | 1.2.6
# MacでUnity 6000.0.23f1の場合 # generateAdditionalReportsを付与することで、SonarQube, Cobertura and LCOV形式で出力する $ /Applications/Unity/Hub/Editor/6000.0.23f1/Unity.app/Contents/MacOS/Unity \ -projectPath <path-to-project-path> \ -batchmode \ -testPlatform editmode \ -runTests \ -debugCodeOptimization \ -enableCodeCoverage \ -burst-disable-compilation \ -coverageResultsPath <path-to-codecoverage-result> \ -coverageOptions "generateAdditionalReports;"
↓実際に実行した例
$ tree <path-to-codecoverage-result> ├── <ProjectName>-opencov │ └── EditMode │ ├── TestCoverageResults_0000.xml │ └── TestCoverageResults_0001.xml └── Report ├── Cobertura.xml ├── SonarQube.xml ├── Summary.json ├── Summary.md ├── Summary.xml └── lcov.info
アセンブリをフィルタする
コードカバレッジを計測するアセンブリを絞りたいことはよくあると思います。assemblyFilters
を用いることで含めるアセンブリを絞ることができます。+
は含めて、-
は除外です。
# assemblyFiltersにより計測するアセンブリをフィルタする # "Hoge."でアセンブリ名が始まるものを対象にし、"Hoge.Samples."でアセンブリ名が始まるものは計測対象外にする(Globにより指定) $ /Applications/Unity/Hub/Editor/6000.0.23f1/Unity.app/Contents/MacOS/Unity \ -projectPath . \ -batchmode \ -testPlatform editmode \ -runTests \ -debugCodeOptimization \ -enableCodeCoverage \ -burst-disable-compilation \ -coverageResultsPath <path-to-codecoverage-result> \ -coverageOptions "generateAdditionalReports;assemblyFilters:+Hoge.*,-Hoge.Samples.*"
またpathFilters
などもありますので、気になる方は公式ドキュメントを参照してください。
HTML Reportを生成する
CodeCoverage Packageでは下記のようなHTMLレポートを生成することができます。具体的にどのLineがテスト実行されていないか調べるケースでは有用なので、GitHubのArtifactやGCSなどにあげてPRを送ったタイミングでみれるようにするとかはアリかなと思います。
# generateHtmlReportを追加する $ /Applications/Unity/Hub/Editor/6000.0.23f1/Unity.app/Contents/MacOS/Unity \ -projectPath . \ -batchmode \ -testPlatform editmode \ -runTests \ -debugCodeOptimization \ -enableCodeCoverage \ -burst-disable-compilation \ -coverageResultsPath <path-to-codecoverage-result> \ -coverageOptions "generateAdditionalReports;generateHtmlReport"
octocovを利用してGitHubに通知を行う
octocov
はコードメトリクスを収集するためのツールキットです。今回は GitHubにコードメトリクスをコメントで通知する + Big Queryにデータをアップロードするために利用します。
octocov is a toolkit for collecting code metrics (code coverage, code to test ratio, test execution time and your own custom metrics).
// DeepL翻訳
octocovは、コード・メトリクス(コード・カバレッジ、コード対テスト比、テスト実行時間、独自のカスタム・メトリクス)を収集するためのツールキットです。
カバレッジレポートのフォーマットは沢山あるのですが、octcovは以下に対応しています。
- Go coverage
- LCOV
- SimpleCov
- Clover
- Cobertura
- JaCoCo
UnityのCodeCoverage
はLCOV
を吐けるので、LCOV
を利用すると良いでしょう。またcoverlet.collector
を利用したdotnet test
でもLCOV
を吐けます。
設定ファイルの作成
octocovには設定ファイルを利用することで様々な設定ができます。場所はどこでも良いのですが.octocov.ymlファイルを作成して中身を記述してください。
詳細はreadmeを見てみて欲しいのですが、私がよく利用する設定を載せておきます。
github.com
# .octocov.yml # lcov.infoを読み込む(UnityのCode Coverage Packageで出力先をCodeCoverageに設定する) coverage: paths: - CodeCoverage/Report/lcov.info # テストの実行時間として利用するsteps (Actions側でnameを'Unity Test'にしている場合) testExecutionTime: steps: - 'Unity Test' # codeとtestの割合を計算するのに利用するファイル codeToTestRatio: code: - '**/*.cs' - '!**/Tests/**/*.cs' test: - '**/Tests/**/*.cs' # プルリクエストにコメントをつける comment: if: is_pull_request hideFooterLink: true # JobのSummaryを表示する summary: if: true # 差分を表示するためにArtifactを利用(後ほどBig Queryに置き換える) report: if: is_default_branch datastores: - artifact://${GITHUB_REPOSITORY}/dotnet-test-report diff: datastores: - artifact://${GITHUB_REPOSITORY}/dotnet-test-report
GitHub Actionsのワークフロー作成
Unityでテストを実行して、octocov
でPRにコメントをするワークフローを作成します。
# .github/workflows/test.yml name: test on: push: branches: main pull_request: branches: main workflow_dispatch: jobs: test: runs-on: ubuntu-latest permissions: contents: read pull-requests: write actions: read checks: write env: LCOV_DIR: ${{ github.workspace }}/CodeCoverage steps: - uses: actions/checkout@v4 - name: Unity Test step: run: | # ここで下記のようにUnityをバッチモードで立ち上げてテストを実行しなければならない # 以下の書き方だと動作しないので注意してください /Applications/Unity/Hub/Editor/6000.0.23f1/Unity.app/Contents/MacOS/Unity \ -projectPath . \ -batchmode \ -testPlatform editmode \ -runTests \ -debugCodeOptimization \ -enableCodeCoverage \ -burst-disable-compilation \ -coverageResultsPath "$LCOV_DIR" \ -coverageOptions "generateAdditionalReports;" - uses: k1LoW/octocov-action@v1 with: config: .octocov.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }}
ただしUnityを用意しないといけないのですが、SelfHosted Runnerを個人で用意するのはかなり大変です。そこで利用させていただくのがGame CI
です。
www.hanachiru-blog.com
Game CIの利用
Game CI側でUnityを用意してくれているので、それをありがたく利用させていただきます。セットアップの仕方は先ほど貼った記事に書いてあるので省くのですが、game-ci/unity-test-runner@v4
を使う際にinputs
にcoverageOptions
を用意してくれています。
github.com
Code Coverage Options With Combined Coverage Results · Issue #181 · game-ci/unity-test-runner · GitHub
github action check status is "neutral" when tests pass · Issue #220 · game-ci/unity-test-runner · GitHub
# .github/workflows/test.yml name: test on: push: branches: main pull_request: branches: main workflow_dispatch: jobs: test: runs-on: ubuntu-latest permissions: contents: read pull-requests: write actions: read checks: write steps: - uses: actions/checkout@v4 - name: Unity Test uses: game-ci/unity-test-runner@v4 env: UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} with: githubToken: ${{ secrets.GITHUB_TOKEN }} unityVersion: 6000.0.23f1 # PlayModeとEditModeのTestを実行 testMode: all # CodeCoverageの出力先を指定できないよう(-coverageResultsPathでも制御できなかった)なので、CodeCoverage/Report/lcov.infoに固定で出力されているので使う coverageOptions: generateAdditionalReports;dontClear customParameters: -debugCodeOptimization -enableCodeCoverage -burst-disable-compilation - uses: k1LoW/octocov-action@v1 with: config: .octocov.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }}
またaction.yml
のoutputにcoveragePath
を用意してくれているので、それを使うこともできます。ただoctcov
側に環境変数を渡せなかった(多分)ので、CodeCoverage/Report/lcov.info
を直書きしてます。また-coverageResultsPath
を用いても出力先を制御できなさそうでした。
github.com
# 利用する例 - uses: actions/upload-artifact@v3 if: always() with: name: Coverage results path: ${{ steps.myTestStep.outputs.coveragePath }}
動作している様子
正しく動作していれば、PRを作成すると以下のような表示がなされるはずです。
BigQueryの構築
BigQueryはGCPが提供するデータ分析プラットフォームです。今回はコードメトリクスデータを保存するために利用します。
GCPのサービスアカウントの作成
octocovからBig Queryへコードメトリクスデータを送るにあたって、まずはサービスアカウントを作成する必要があります。
cloud.google.com
権限をどうするかはちゃんとご自身で調べて欲しいですが、私の場合は以下を利用しました。
- roles/bigquery.jobUser
- roles/bigquery.dataEditor
https://cloud.google.com/bigquery/docs/access-control?hl=ja
またどんな権限が必要かはoctcovのreadmeに載っています。
- bigquery.datasets.get
- bigquery.tables.get
- bigquery.tables.updateData
- bigquery.jobs.create
- bigquery.tables.getData
https://github.com/k1LoW/octocov?tab=readme-ov-file#bigquery
https://github.com/k1LoW/octocov?tab=readme-ov-file#use-bigquery-table-as-datastore
サービスアカウントが無事作成できたら、鍵(JSON)を作成しておきましょう。
作成できたら、GitHubのsecretsにGOOGLE_APPLICATION_CREDENTIALS_JSON
などとして入れておきましょう。
Big Queryであらかじめデータセットを用意しておく
GCPのBigQuery上にあらかじめデータセットを用意しておきます。コンソール上でも良いですし、コマンドでも好きなように作成してください。
テーブルは後ほど作成するので、自身で定義しなくてOKです。
作成したテーブルに情報を追加していくように、先ほどGitHub Artifactにあげるように設定した.octocov.yml
をBig Queryにあげるように修正します。bq://[project ID]/[dataset ID]/[table]
のように指定してください。
# .octocov.yml ... # 差分を表示するためにBig Queryを利用する # bq://[project ID]/[dataset ID]/[table] report: if: is_default_branch datastores: - bq://hogehogehoge/octocov_sandbox/reports diff: datastores: - bq://hogehogehoge/octocov_sandbox/reports
table
の名前は好きに決めちゃってしまってOKです。
またGitHub上で実行するためにはGOOGLE_APPLICATION_CREDENTIALS_JSON
を先ほど取得したサービスアカウントの鍵として設定する必要があります。
- uses: k1LoW/octocov-action@v1 with: config: .octocov.yml env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_REPOSITORY: ${{ github.repository }} GOOGLE_APPLICATION_CREDENTIALS_JSON: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS_JSON }}
https://github.com/k1LoW/octocov?tab=readme-ov-file#bigquery
Big Queryにてテーブルのmigrateをする
migrateするにあたって、CLIツールをインストールしておきます。
$ brew install k1LoW/tap/octocov
BigQueryを活用するためにはテーブルのmigrateが必要になります。.octocov.yml
がカレントディレクトリに含まれる状態で以下のコマンドを叩きます。
$ octocov migrate-bq-table
また環境変数等の設定も必要なので、雑に以下のようなPowerShell
を用意しておきました。(私は生粋のPowerShell教なので、bash/zshは書きません)
#!/usr/bin/env pwsh #Requires -Version 7.4 $PSNativeCommandUseErrorActionPreference = $true $ErrorActionPreference = "Stop" $env:GOOGLE_APPLICATION_CREDENTIALS_JSON = @" { "type": "service_account", ... } "@ octocov migrate-bq-table
具体的なSchemaは下記に載っています。
octocov/docs/bq/schema/reports.md at main · k1LoW/octocov · GitHub
Looker Studioにより可視化する
Looker Studio
を用いることで、簡単にBig Queryに保存したデータの可視化をすることができます。詳細は書きませんが、直感的にわかるように作られているのでボタンをポチポチしていたらできると思います。
結果
PRを出す度にテスト + コードメトリクス計測を行い、その結果をGitHub上で表示してBigQueryにあげることができました。またその推移をLooker Studio上で確認することができます。さらにHTMLレポートもどこかにホスティングしていい感じにみれるようにすれば、どのLineが具体的にテストが通っていないかの確認もできます。
考察
分析基盤を構築した後、これをどうやって活用していくのかもとても重要な要素かと思います。正直私自身もまだ模索している中です。
特にゲームクライアントだとテスト自体あまり活用されていないことも多々あるのかなと思うので、是非上手に活用してみてください。