この記事は「Kover でらくらく! Android のカバレッジレポート出力」の補足となります。
Gradle でカバレッジレポートを出力する設定方法は、Codecov 公式で JaCoCo Plugin を使う方法が紹介されています。しかし Android のローカル単体テストを対象とする場合の設定が難解です。近年、Kotlin 公式のプラグインである Kover がリリースされ、それを使用することで、 最短2行のシンプルな記述でカバレッジレポートの出力を設定できるようになりました。
と言いましたが、JaCoCo Plugin はどのくらい難解だったのかを解説します。
開発スタイルおよび環境
この記事では元の記事と同様の開発スタイルおよび環境を想定しています。
- Android プロジェクト
- マルチモジュールを前提に解説しています。
- シングルモジュールでもそのまま使えます。
-
ローカル単体テスト
- Robolectric を使ったローカル単体テストにも対応しています。
- インストゥルメント化単体テストは今回の対象外です。
- Gradle 8.1.1
- Android Gradle Plugin 8.0.2
- Kotlin 1.8.22
バイナリ形式のカバレッジレポートを出力する
プロジェクトの build.gradle を編集して設定します。
まず、すべてのモジュールで jacoco
プラグインを使うようにします。
allprojects {
apply plugin: 'jacoco'
}
すると単体テストタスク実行時に testDebugUnitTest.exec
がモジュールごとに出力されるようになります。(Debug の部分は Build Variant)
./gradlew testDebug
find . -type f -name *.exec
./app/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./feature/home/usecase/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./feature/home/presentation/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./feature/info/compose/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./feature/info/usecase/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./feature/info/presentation/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./data/repository/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./data/local/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./data/remote/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
./viewCommon/build/outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec
このファイルの中身はバイナリ形式となっています。
このままでは人間は読めませんし、Codecov でも非対応のフォーマットとなっています。
バイナリ形式のカバレッジレポートを HTML および XML 形式のカバレッジレポートに変換する
Codecov は XML 形式のカバレッジレポートに対応しています。また、Gradle タスクのデバッグや Codecov を使わない運用のために、HTML 形式にも変換します。
そのために JaCoCo プラグインで以下のような変換を行います。
kt ファイル + class ファイル + exec ファイル ⇒ HTML/XML 形式のカバレッジレポート
プロジェクトの Gradle ファイルに以下の記載を追加することで実現できます。
// jacocoReport タスクを作成
tasks.register("jacocoReport", JacocoReport) {
{
// XML 形式 と HTML 形式の両方のカバレッジレポートを出力する
reports {
xml.required.set(true)
html.required.set(true)
}
// 全モジュールの kt ファイルのディレクトリ一覧
def mySourceDirectories = subprojects.collect { project ->
"${project.projectDir}/src/main/java"
}
// 全モジュールの class ファイルのディレクトリ一覧
def myClassDirectories = subprojects.collect { project ->
"${project.buildDir}/tmp/kotlin-classes/debug"
}
// exec ファイル一覧
def execFileTree = fileTree(dir: rootDir, includes: [
"**/testDebugUnitTest.exec"
])
// それらを JacocoReport タスクの設定値とする
sourceDirectories.setFrom(mySourceDirectories)
classDirectories.setFrom(myClassDirectories)
executionData.setFrom(execFileTree)
}
これで jacocoReport
タスクの実行で XML 形式 と HTML 形式のカバレッジレポートが出力されます。
./gradlew jacocoReport
出力先はこちらです。
XML 形式 build/reports/jacoco/jacocoReport/jacocoReport.xml
HTML 形式 build/reports/jacoco/jacocoReport/html/index.html
HTML 形式のレポートはこのように出力されます。
jacocoReport タスクだけでカバレッジレポートを出力する
これまでの設定ではカバレッジレポートを出力するために、2つのタスク実行が必要です。
./gradlew testDebug
./gradlew jacocoReport
1タスクで完結するために、タスクの依存関係を設定します。
tasks.register("jacocoReport", JacocoReport) {
// 全モジュールの testDebugUnitTest タスク一覧
def tasks = subprojects.findAll { project ->
// モジュールでないディレクトリも列挙されるので、build.gradle が存在するものだけを対象とする。
project.file("build.gradle").exists()
}.collect { project ->
"$project.path:testDebugUnitTest"
}
// それらが完了してから、このタスクを実行する
dependsOn(tasks)
// 以下、前の節ど同じ
}
これで jacocoReport
タスクだけでカバレッジレポートが出力されるようになります。
./gradlew jacocoReport
カバレッジレポート対象クラスを制限する
例えば ViewModel クラスだけなど、カバレッジレポート対象クラスを制限する方法も紹介します。freeTree メソッドを使い、対象クラスの class ファイルだけ指定すれば良いです。
// jacocoReport タスクを作成
tasks.register("jacocoReport", JacocoReport) {
{
// 全モジュールの対象 class ファイル一覧
def classFileTrees = subprojects.collect { project ->
fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/debug",
includes: ["**/*ViewModel.class"]
)
}
classDirectories.setFrom(classFileTrees)
}
まとめ
この記事では JaCoCo Plugin で Android のローカル単体テストのカバレッジレポート出力の設定方法を解説しました。最短2行で設定が完了する Kover に対して、JaCoCo Plugin では Gradle のタスクを作成したり、kt ファイル、class ファイル、exec ファイルのパスを指定する必要があったりと難解です。すでに設定済みならばともかくとして、これから設定する場合は Kover の方がおすすめです。