jacoco で gradle のマルチモジュール構成のカバレッジを集計する

N年ぶり、M回目で gradle を利用しているマルチモジュールプロジェクトのコードカバレッジ集計を jacoco で設定する方法を調べた。
毎回毎回調べてるのでいい加減メモを残そうというもの。
と思い、調べてみるとちょっと前にやってた方法は結構泥臭くやっていた気がしていたのだけれど、以下2つの Gradle のページだけで解決してしまった。

The JaCoCo Plugin
Reporting code coverage with JaCoCo Sample

TL;DR

Root Project

plugins {
  // ...
  id("jacoco")
}

// ...

jacoco {
    toolVersion = "0.8.7"
}

tasks.register<JacocoReport>("codeCoverageReport") {
	subprojects {
		val subproject = this
		subproject.plugins.withType<JacocoPlugin>().configureEach {
			subproject.tasks.matching { it.extensions.findByType<JacocoTaskExtension>() != null }.configureEach {
				val testTask = this
				sourceSets(subproject.sourceSets.main.get())
				executionData(testTask)
			}

			subproject.tasks.matching { it.extensions.findByType<JacocoTaskExtension>() != null }.forEach {
				rootProject.tasks["codeCoverageReport"].dependsOn(it)
			}
		}
	}

	reports {
		xml.isEnabled = true
		html.isEnabled = true
	}
}

Sub Projects

テストカバレッジを集計したいサブプロジェクトに以下を適用する

plugins {
    // ...
    id("jacoco")
}

// ...

jacoco {
    toolVersion = "0.8.7"
}

tasks.jacocoTestReport {
    dependsOn(tasks.test)
}

カバレッジ集計

そして以下を実行すれば、 /build/reports/jacoco/codeCoverageReport/ にレポートが出力される。

./gradlew clean codeCoverageReport

タスクの依存関係

上記のようなタスクの依存関係になっていて、それぞれのタスクは単独で実行可能になっている。 (実行されると dependsOn で依存しているタスクも実行される。)
codeCoverageReport は唯一新規に定義するタスクで他のものは、 プラグインのものに dependsOn を設定しているだけである。

codeCoverageReport

JacocoReport にもある通りなんですが、特に注目するプロパティは、以下の2つ

executionData は jacoco プラグインを適用すると自動で作成してくれる build/jacoco/xxx.exec を与えるとカバレッジを集計してくれる。
カバレッジのデータを与えるだけではだめで、対象となるソースコードも与える必要があるので sourceSets に対象となるソースコードを適用してあげる。

これだけで、すべての jacoco プラグインが適用されているサブプロジェクトのテストカバレッジレポートを1つのレポートとして集計することができる。

その他

Kotlin のプロジェクトで jacoco 0.8.6以前を利用した場合

今回に直接関係ないが、自分の現在の環境ではデフォルトで jacoco のバージョンが 0.8.6.x の最新版が適用されていた。
これで実行すると、 https://github.com/jacoco/jacoco/issues/1155 にある通り、Kotlin 1.5系での形式を読み込めずにタスクがエラーになってしまうケースに遭遇した。

./gradlew clean codeCoverageReport --stacktrace

のように --stacktrace オプションでエラーの内容を見ることができるのでそこでわかる。