nashcft's blog

時々何か書く。

Kover 触ったメモ

DroidKaigi 2023 公式アプリなどで触ったので所感などを書き残しておく。この記事の内容は Kover 0.7.3 時点の機能に基づいている。

書いてたら思ったより長くなったので先に結論を書いておくと「簡単なので使う場合は各自でいい感じにやっといてください」になる。

Kover とは

JetBrains が開発している code coverage の toolset。要するに JaCoCo みたいなやつ。今回は Gradle plugin について扱うが他に CLI も存在する。

使用メモ

使い方に関しては解説や要約が必要なほど多機能なわけではないので、何ができるかとかのちゃんとした説明は docs を読んでほしい。

Tasks

  • koverHtmlReport: 計測結果を html で出力
    • 手元で見る時はこれ
  • koverXmlReport 計測結果を xml で出力
    • codecov とかに食わせる時はこれ。
  • koverBinaryReport: 計測結果を binary file で出力
    • そういう形式を要求する何かに食わせる時用 (使ったことあるので具体例を知らない)
  • koverLog: 計測結果を task のログとして出力
  • koverVerify: 設定した rule を満たすかの検査
    • 設定については後述
    • rule を満たさない場合は task が失敗する

Android module に対しては上記の task 名の末尾に build variant が付く。

設定まわり

koverReport の中で記述する。 以下は大雑把な設定項目の説明:

  • filter: 計測対象の追加・除外の設定
    • class の指定に関しては fully qualified な class 名が求められるので package 部分から記述する必要がある
    • wildcard の記述で *** が使えるがこれらの違いは無いとのこと (?)
  • verify: verification task に関する設定
    • 評価する単位 -> 全体、 class 毎、 package 毎
    • OK とする coverage の下限・上限
    • 計測値の基準 (lines / bytecode instructions / branches) と単位 (covered or missed に対する count / percentage)
  • defaults: default task に対する設定
    • filterverify はこの中でも設定可能
    • 他に出力形式毎の諸々の設定をすることが可能
  • androidReports
    • Android module のそれぞれの build variant に対する設定
    • 記述できる設定については defaults と同じ

Coverage report の集約

:foo:bar があったとして、 :foo の方の build.gradle で以下のように :bar への依存を追加すると、 :foo の方で kover task を実行した時に :bar のものとまとめられた coverage report が作成される:

// foo/build.gradle
dependencies {
  kover(projects.bar)
}

kover() で依存に追加される module は 同様に kover gradle plugin を適用する必要がある。また、 report の集約を行う際、 koverReport による設定は各 module のものではなく集約を行う (i.e. kover() による依存の設定を記述した) module のもののみが適用される。

所感

どこで report を集約するか

Kotlin/JVM や KMP で Android を考慮しない module のみから構成される project であれば root project で集約して良いと思う。 Android の考慮が入ると割と悩ましい。というのも root project は build variant を持っていないのでここで default report に build variant を merge させるということができないからである。別の場所で集約するとなると、 application project の場合は app module のような代表的な module が候補として検討できるが、 library project の場合は全てを集約した all-in-one module でも存在しない限りそういう候補は project 内で挙げにくく、 report 集約のためにわざわざ専用の module を作るかと言うとそれも微妙では? と感じる。また application project でも複数の app module を持っている、 product flavor の構成によって一つの build variant で全ての実装に対する coverage report を取得することができないといった場合には同様の課題が発生すると思われる。

現時点 (0.7.3) では複数の build variant に対する report を1つに集約する設定方法みたいなものは無さそうだが、複数の build variant をまとめた custom report variant を作成するという機能が以下の issue で検討されているので、将来的にはなんとかなるかもしれない。でも自分の持っていない build variant を解決できるものなのだろうか...?

github.com

設定の記述で楽をしたいが...

触ってみた感じの印象として今のところ Kover の DSL は単純で習得しやすい一方で素朴な書き方しかできず、素直に書くと楽したくなるところで面倒が発生するなあというものがある。具体的には report の集約を行っている場合、新しい module を追加する度に追加された module に kover gradle plugin を適用するのと集約 module の dependencies に kover(projects.newModule) を追加するのを忘れずに行う必要があるという点。これは地味にダルいし人間のやることなので忘れてしまうこともあるだろうしでどうにかしたいが、現時点では Kover DSL 自体はこれを解決する機能を提供していない。

今年の DroidKaigi 公式アプリでは Kover の設定に関する convention plugin が実装されていたので、そこに上記の課題を何とかするための実装を追加して、 report を集約する module (i.e.:app-android module) 以外では Kover に関して気にしなくて良くなるようにした。

github.com

この追加実装では以下の3つの操作をしている*1:

  1. この convention plugin を適用した module に kover gradle plugin を適用する
  2. この convention plugin を適用した module 以外の module に kover gradle plugin を適用する
  3. この convention plugin を適用した module に対して、 2. の操作を行った module を report の集約対象として依存に追加する

ただし、レビューコメントでも指摘されているようにこのアプローチは Gradle の configuration cache 関連で将来導入されるであろう project isolation と思い切り衝突することをしている。現状これでも configuration cache を効かせられるし、今後このようなユースケースに対して project isolation を守りつつ同様の設定ができるようにになる API が追加される可能性もあるが、あくまで今の内だけの手段として捉えておいた方が良いのではないかと思う。

Configuration cache と project isolation について参考:

ところで multi-module project での設定を簡単にするための機能を追加しようという issue が作成されているので、この設定記述の問題も Kover 側で何とかしてくれそうな雰囲気がある。 Custom report variant で root project から各 subproject の build variant をうまく扱えるようになったら Android project でも root project に設定を全部書いて済ませられるようになるのではないかと期待している。

github.com

とは言え今の時点ではどうすりゃいいのさとなるが、個人的には多少の手間を飲み込んで report 集約の依存の記述は集約場所の build.gradle に直接記述し、 plugin の適用と共通で使える koverReport の設定を convention plugin 化して、 DroidKaigi 2023 公式アプリの AndroidFeaturePlugin.kt みたいな各 module 共通で適用する plugin をまとめて適用する convention plugin に追加して使うくらいがいいんじゃないかなと思う。こうするとおまけとして kover gradle plugin を適用した module 全てに koverReport の設定が入るので module 単位で kover reoprt task を実行した時も集約した report を作る時と同じ設定で動くようになる。 Report ファイルの生成は module 個別に行うことはまずないだろうが、 verification は DSL に module 単位での評価という設定は無いので、 module 単位で基準を満たす or not を見られるようになるのは便利かもしれない... のだが、実は 0.7.3 の時点では全ての report variant 向けとなるはずの koverReport 直下の verify による verification rule の設定が Android build variant に対応する report task には適用されないというバグがあり、 Android project ではこのように設定を共通化しても verification rule が共有できない場合がある*2:

github.com

おわりに

だらだらと Kover を触った所感を書いた。 API がわかりやすく簡単に設定を書けるので、 JaCoCo で自作 merge task を作って頑張ってるようなところではこちらに移行した方が幸せになれるんじゃないかと思う。ただし Android project で flavor まわりの構成が複雑だと現時点ではうまくやるにはまだ機能不足に感じるので、そういう場合は頑張って gradle script を書いてどうにかなることを祈るか、もう暫く様子を見るのがいいかもしれない。

*1:2. と 3. に関して、実際は今回行った実装では考慮不足で module 以外の階層部分 (:core とか) にも操作が適用されてしまい色々無駄が発生しているのだが、結果に影響は出ないからいいやということでサボっている

*2:Default report task には問題の verification rule が適用されるので、 kover gradle plugin を適用する全ての module が同一の build variant を持っているならば、それを default task に merge した上で default report task を使用することで問題を回避し verification rule を共有できる