octocovとKoverを使ってテストカバレッジをPull Requestにコメントする
/ 8 min read
Updated:Table of Contents
はじめに
こんにちは、最近は新しい職場でAndroid開発をメインにお仕事しています。
その新しい職場でみんなでチーム目標を決める時に、ユニットテストをもっと書きたいねという話がでました。
どうすればテストを書くのかなと考えた時に、前職のRailsプロジェクトではoctocovを使って、カバレッジが低いとマージさせない仕組みがあったので、それをAndroidプロジェクトに応用できないかと思い、試しに個人で開発しているKMP(Kotlin Multi Platform)プロジェクトで試してみました。
色々試してある程度分かってきたので設定方法について紹介します。使用したコードはGitHub上に公開しています。
octocovとKover
今回使用するoctocovとKoverについて説明します。
cotocov
octocovはコードのメトリクス(カバレッジや実行時間など…)を収集してくれるGo製のツールです。
CLIとしても使えるのですが、今回はGitHub Actions上で使うために公式で準備されているoctocov-actionを使います。
GitHub - k1LoW/octocov-action: :octocat: GitHub Action for octocov
Kover
KoverはKotlin公式のテストカバレッジを収集するツールです。
ちょっと前だとJaCoCoを使ってテストカバレッジを収集したりすることも多かったみたいですが、今回は公式のツール & 設定が簡単らしいという理由でKoverを使うことにしました。
octocovは色々なカバレッジのフォーマットに対応していて、JVM系だとJaCoCo形式のフォーマットに対応しています。なぜ、JaCoCoではなくKoverで良いかというと、実はKoverではXML形式でカバレッジを出力すると、そのXMLがJaCoCoフォーマットになっているのでKoverで大丈夫という感じです。
実装
方針としてはconvention pluginとしてKoverを登録しそこからプロジェクト全体に適用します。
まずはKover関連のライブラリを追加します。使用するバージョンは0.9.1になります。
0.7.xから0.8.3に上がったタイミングで設定方法に変更があるので、他記事などを参考にする時はバージョンに注意して下さい(公式のマイグレーションガイドもあるのでそちらも参考になります)。
Version Catalogに追加し、convention pluginで読み込みます。
[versions]kover = "0.9.1"
[libraries]koverGradlePlugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
[plugins]kotlinxKover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
[bundles]plugins = [ "koverGradlePlugin"]dependencies { implementation(libs.bundles.plugins)}プロジェクト全体にセットしたいのでプロジェクト直下で読み込みます。
plugins { alias(libs.plugins.kotlinxKover) apply false}KoverのカスタムGradleプラグインを作ります。ほぼDroidKaigi2024のままです。
class KoverPlugin: Plugin<Project> { override fun apply(target: Project) { val koverPlugin = "org.jetbrains.kotlinx.kover" with(target) { pluginManager.apply(koverPlugin)
// プロジェクト内の各モジュールに適用するためサブプロジェクトが呼ばれる前にKoverをapplyする。 rootProject.subprojects { if (this@subprojects.name == target.name) return@subprojects this@subprojects.beforeEvaluate { this@subprojects.pluginManager.apply(koverPlugin) } target.dependencies.add("kover", this@subprojects) }
extensions.configure<KoverProjectExtension>("kover") { reports { total { xml { // カバレッジのレポートをわかりやすい場所に出力します(rootのプロジェクト直下)。 xmlFile.set(rootProject.file("coverage.xml")) } } filters { // 評価されてほしくないコードを除外します(自動生成系)。 excludes { classes( // classes generated by Hilt "Hilt_*", "*_Factory", "*_HiltModules*", "*Module_Provide*Factory", // compose previews "*Preview*Kt", ) } } } } } }}Koverの設定はできたのでこれをカスタムGradleプラグインとして登録します。
gradlePlugin { plugins { register("kover") { id = "love.aespa.nemomemo.kover" implementationClass = "KoverPlugin" } }}ここがいまいちよく分かってないのですが、DroidKaigi2024だとメインのアプリモジュールにのみ適用していたのでcomposeAppにのみ設定を追加しています(おそらくKoverのカスタムGradleプラグインを作った時に、サブプロジェクトにも勝手に適用するようにしたので、メインのモジュールにだけ適用しているということかな…と勝手に解釈しています)。
plugins { id("love.aespa.nemomemo.kover")}ここまでの設定で./gradlew tasks を実行するとkoverXmlReport のタスクが追加されていると思います。このタスクを実行するとプロジェクト直下にcoverage.xml が出力されるのでこのファイルをoctocovで読み込むようにします。
次にoctocovの設定です。プロジェクト直下に.octocov.ymlを作成します。色々設定があるのですが、今回は以下のように設定しました。
coverage: acceptable: 10% ## カバレッジが10%以上だったらOK paths: - coverage.xml ## カバレッジレポートのパスdiff: ## 差分を比較 datastores: - artifact://${GITHUB_REPOSITORY}comment: ## PRでコメントする if: is_pull_requestreport: ## デフォルトブランチでデータを取得後GitHubのartifactに保存し差分を比較する if: is_default_branch datastores: - artifact://${GITHUB_REPOSITORY}summary: ## CIのsummaryに実行結果を出力します if: trueCIで動かすためにGitHub Actionsのワークフローを追加します。
name: Test
on: pull_request: push: branches: - main
permissions: pull-requests: write
jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: set up JDK 17 uses: actions/setup-java@v4 with: java-version: "17" distribution: "temurin" cache: "gradle"
- name: Setup Gradle uses: gradle/actions/setup-gradle@v4
- name: UnitTest run: ./gradlew allTests
- name: Generate Coverage Report run: ./gradlew koverXmlReport
- uses: k1LoW/octocov-action@v1注意点は、permissionsのpull-requests: write です。これがないとoctocovがPull Requestにコメントをする権限が足りずに、CI上で403になりコメントされません(CI自体はエラーになりません)。
Skip commenting report to pull request: POST https://api.github.com/repos/Tatsumi0000/nemomemo/issues/2/comments: 403 Resource not accessible by integration []CIが動くとPull RequestとCIのsummaryにコメントをします。
Pull Requestにコメント
CIのsummaryにコメント
もし.octocov.ymlのacceptableに満たない場合はCIが失敗します。
acceptableを60%にしているのでCIが落ちる
終わりに
octocovとKoverを使ってカバレッジのレポートを表示するようにしました。
初めてoctocovとKoverの設定を書いたのですが、非常に簡単に使うことができたので良かったです。
導入方法は分かったので実際に業務でも試してみたいなと思います!
参考文献
- Kotlin/kotlinx-kover
- k1LoW/octocov: octocov is a toolkit for collecting code metrics (code coverage, code to test ratio, test execution time and your own custom metrics).
- DroidKaigi/conference-app-2024: The Official Conference App for DroidKaigi 2024
- kotlinx-kover/kover-gradle-plugin/docs/migrations/migration-to-0.8.0.md at main · Kotlin/kotlinx-kover
- kotlinx-kover/kover-gradle-plugin/docs/index.md at main · Kotlin/kotlinx-kover
- octocovで実現できる3つのレポートコメントの方法 - Copy/Cut/Paste/Hatena
- Kover でらくらく! Android のカバレッジレポート出力 #Kotlin - Qiita