目的
Gradleでマルチプロジェクト構成の場合、testタスク実行時のテスト結果やJacocoのコードカバレッジは、通常サブプロジェクトごとに生成されます。
しかし、JenkinsなどのCIツールでテスト結果やカバレッジを集計したり、HTMLレポートの生成などを考えると、場合によってはプロジェクト全体の実行結果を集計する必要が出てきます。
ルートプロジェクト側にタスクを定義し、サブプロジェクトのテスト実行結果を集計したレポートを出力する方法をまとめます。
結論
下記の設定で、gradle check testReport jacocoReport
と実行すればサブプロジェクトのJUnitレポート、Jacocoレポートの集計結果が出力されます。
subprojects {
apply plugin: 'java'
apply plugin: 'jacoco'
tasks.withType(AbstractCompile).each { it.options.encoding = 'UTF-8' }
sourceCompatibility = 1.8
targetCompatibility = 1.8
// ...(Omitted)...
}
task testReport(type: TestReport) {
reportOn subprojects.tasks.test.binResultsDir
destinationDir file("$buildDir/reports/tests/test")
}
apply plugin: 'jacoco'
task jacocoMerge(type: JacocoMerge) {
executionData project(':child1').tasks.test.jacoco.destinationFile
executionData project(':child2').tasks.test.jacoco.destinationFile
}
task jacocoReport(type: JacocoReport) {
dependsOn jacocoMerge
executionData jacocoMerge.destinationFile
sourceSets *subprojects.sourceSets.main
}
実行結果集計用のタスククラス
Gradleには、テスト実行結果、Jacocoカバレッジをそれぞれ集計するためのタスククラスがデフォルトで用意されています。
TestReport
TestReport - Gradle DSL Version 4.7
https://docs.gradle.org/4.7/dsl/org.gradle.api.tasks.testing.TestReport.html
テスト実行結果のHTMLレポート生成を行うためのタスククラスです。
task testReport(type: TestReport) {
reportOn subprojects.tasks.test.binResultsDir
destinationDir file("$buildDir/reports/tests/test")
}
reportOn
に指定したディレクトリからテスト実行結果を読み取って、destinationDir
にHTMLレポートを出力します。gradleの出力系タスクにしては珍しく(?)デフォルト出力先が決まっていないので、destinationDir
は設定必須です。
注意点
下記のように、reportOn
にtestタスク自体を指定することも可能です。むしろこの書き方のほうがgradleらしく見えます。
reportOn subprojects.tasks.test
ただし、この場合は定義したタスクから各testタスクへの依存関係が設定されてしまいます。
このため、タスク実行時にサブプロジェクトすべてのtestタスクが実行され、さらにテストケースが失敗した場合にはタスクがスキップされてしまいます。
通常のtestタスク実行時は、テストケースの成否に関わらずHTMLレポートも出力されるはずなので、ディレクトリを指定したほうが想定通りの動きになるはずです。
参考URL: TestReport not generated if one of executed tests fails - Old Forum - Gradle Forums
JacocoMerge
JacocoMerge - Gradle DSL Version 4.7 https://docs.gradle.org/4.7/dsl/org.gradle.testing.jacoco.tasks.JacocoMerge.html
サブプロジェクトごとのカバレッジを集計するためのタスククラスです。
task jacocoMerge(type: JacocoMerge) {
executionData project(':child1').tasks.test.jacoco.destinationFile
executionData project(':child2').tasks.test.jacoco.destinationFile
}
executionData
に指定したjacocoの実行結果ファイルをマージした実行結果ファイルを出力します。デフォルトの出力先は${buildDir}/jacoco/${タスク名}.exec
です。
注意点
executionData
は可変長引数を受け取りますが、TestReportクラスのreportOn
と違ってIterableを受け付けません。サブプロジェクトの指定を1行で済ませたい場合は、下記のようにGroovyのリスト展開演算子を使う必要があります。
executionData *subprojects.tasks.test.jacoco.destinationFile
ただし、jacocoMergeタスクはexecutionData
に存在しないファイルが指定されていると実行時にエラーとなります。サブプロジェクトすべてにテストがある場合は特に問題ありませんが、テストがないプロジェクトがある場合はexecutionData
に含まれないように設定する必要があります。
また、TestReportタスククラスのようにtestタスク自体を指定することも可能ですが、同様に依存関係が自動的に設定されてしまいます。これも避けたほうが良いでしょう。
executionData project(':child1').tasks.test
JacocoReport
JacocoReport - Gradle DSL Version 4.7 https://docs.gradle.org/4.7/dsl/org.gradle.testing.jacoco.tasks.JacocoReport.html
Jacocoの実行結果からレポートを生成するためのタスククラスです。
javaプラグインとjacocoプラグインの両方をapplyすると自動的に定義されるjacocoTestReport
というタスクがありますが、これもJacocoReportタスククラスを元に定義されています。
task jacocoReport(type: JacocoReport) {
executionData jacocoMerge.destinationFile
sourceSets *subprojects.sourceSets.main
}
executionData
で、jacocoMergeタスクで生成した集計済みの実行結果ファイルを指定します。sourceSets
は何故かドキュメントに載っていませんが、ソースセットを指定するだけでsourceDirectories
、classDirecrtories
の両方を設定してくれます。
参考URL: gradle/JacocoReportBase.java at master · gradle/gradle
まとめ
Gradleのサブプロジェクトのレポート集計については、あまり利用シーンが少ないのか、細かい部分でAPIがイマイチな印象を受けます。特に依存関係周りはうまくいっていると気付きにくいので注意が必要ですね。
参考URL
GradleでマルチプロジェクトのJUnit ReportとJacoco Reportを出す - clash_m45の開発ブログ
Gradleのマルチプロジェクトのテスト結果とカバレッジをそれぞれ1つにまとめる - Qiita