GradleでマルチプロジェクトのJUnit ReportとJacoco Reportを出す

Gradleのマルチプロジェクトを使う必要があって、だいぶハマったので記事に残しておこうと思う。

マルチプロジェクト構成

  • root
    • myproject
    • myproject-test

ここでは、実際のディレクトリとしてフラットに存在する例

$ ls
myproject  myproject-test  root

root

親プロジェクト。 最初はsettings.gradleしか持っていない状態。

settings.gradle

includeFlat "myproject", "myproject-test"

includeで階層型になる。参考

myproject

プロダクトコードプロジェクト。 ここにもJUnitテストコードがあったりする。

myproject-test

myprojectプロジェクトのテスト用プロジェクト。 単体・結合・総合テスト用JUnitが置かれたりする。

現状の問題点

このままgradle test jacocoTestReportしたら、myproject・myproject-testのbuildにそれぞれレポートが出力される。

  • root
  • myproject
    • build
      • jacoco - test.exec
      • reports
        • jacoco - html - index.html
        • tests - index.html
  • myproject-test
    • build
      • jacoco - test.exec
      • reports
        • jacoco - html - index.html
        • tests - index.html

これだとJenkinsで管理するときにレポートが上手く行かなくて困る。

myprojectとmyproject-testのJUnit,Jacocoレポートを一括、まとめて、統合、結合でレポートしたい!

JUnit Report

23.13. テスト - gradle userguide
-> 例23.14 Creating a unit test report for subprojects

ここに書いてあるんだけど、なんでか英語のまま!(激怒)

root/build.gradle

subprojects {
    apply plugin: 'java'

    test {
        // 別タスクで一括で出力する
        reports.html.enabled = false
    }
}

task testReport(type: TestReport) {
    destinationDir = file("$buildDir/reports/allTests")
    reportOn subprojects.tasks.test
}

はまりどころは、apply plugin: 'java'が必須というところ。 これはGradleの知識がまだ不足してたからだけど、rootからsubprojectを見た時に定義されてないとダメポ

myprojectとかmyproject-testでapply plugin: 'java'してるからrootではいらないかな?

とかいうのが落とし穴でした。

結果レポート出力

ということで、rootにtestReportというTaskを追加したので、実行する。

$ cd root

$ gradle clean test testReport
:myproject:clean
:myproject-test:clean
:myproject:compileJava
:myproject:processResources UP-TO-DATE
:myproject:classes
:myproject:compileTestJava
:myproject:processTestResources UP-TO-DATE
:myproject:testClasses
:myproject:test
:myproject-test:compileJava
:myproject-test:processResources UP-TO-DATE
:myproject-test:classes
:myproject-test:compileTestJava
:myproject-test:processTestResources UP-TO-DATE
:myproject-test:testClasses
:myproject-test:test
:testReport

BUILD SUCCESSFUL

Total time: 19.366 secs
  • root
    • build
      • reports
        • allTests - index.html
  • myproject
  • myproject-test

myprojectmyproject-testにはreportsは出力されてない。OK

f:id:clash_m45:20150124161734p:plain

Jacoco Report

お次はJacoco Reportをまとめて出力します。
maven時代はcovertura使ってたんだけどなんだか最近はjacocoがいいのかな?ってことでこちらを使ってます。

このGithub Gistを参考にしました。
Aggregated Jacoco reports in a multi-project Gradle build

あとは公式説明書
Chapter 34. The JaCoCo Plugin

DSLも見ないと全然わかりません。
DSL - JacocoReport

Projectのプロパティも使うので、DSL見ます。
DSL - Project

root/build.gradle

allprojects {
    apply plugin: 'java'
    apply plugin: 'jacoco'

    repositories {
        jcenter()
    }
}

subprojects {
    test {
        // 別タスクで一括で出力する
        reports.html.enabled = false

        jacoco {
            //execをrootprojectに出力する
            destinationFile = file("$rootProject.buildDir/jacoco/${project.name}.exec")
        }
    }
}

task testReport(type: TestReport) {
    destinationDir = file("$buildDir/reports/allTests")
    reportOn subprojects.tasks.test
}

task jacocoRootReport(type: JacocoReport) {
    dependsOn = subprojects.test
    //レポート対象はmyprojectのソース
    additionalSourceDirs = files(project('myproject').sourceSets.main.allSource.srcDirs)
    sourceDirectories = files(project('myproject').sourceSets.main.allSource.srcDirs)
    classDirectories = files(project('myproject').sourceSets.main.output)
    //レポートのためのexecはjacocoTestReportタスクの設定を利用
    executionData = files(subprojects.tasks.jacocoTestReport.executionData)
    reports {
        html.enabled = true
        xml.enabled = false
        csv.enabled = false
    }
}
  • ハマリポイント
    • rootprojectにもjacoco pluginが必要
      • jacocoRootReportタスクでjacocoのクラスパスを要求されるので、plugin applyしておかないと怒られます。
    • カバレッジ対象はmyprojectのソースにしたい
      • projectのプロパティの使い方がいまいちよくわからないのが問題で、かなりはまった。
    • jacoco execをrootprojectに出したい
      • ハマった原因はやっぱりprojectのプロパティがよくわからなかったから。

結果レポート出力

結果がわかりやすいようにJavaファイルを変更しました。

それではタスク実行。

$ cd root

$ gradle clean jacocoRootReport
:clean
:myproject:clean
:myproject-test:clean
:myproject:compileJava
:myproject:processResources UP-TO-DATE
:myproject:classes
:myproject:compileTestJava
:myproject:processTestResources UP-TO-DATE
:myproject:testClasses
:myproject:test
:myproject-test:compileJava
:myproject-test:processResources UP-TO-DATE
:myproject-test:classes
:myproject-test:compileTestJava
:myproject-test:processTestResources UP-TO-DATE
:myproject-test:testClasses
:myproject-test:test
:jacocoRootReport

BUILD SUCCESSFUL

Total time: 21.308 secs
  • root
    • build
      • jacoco
        • myproject-test.exec
        • myproject.exec
      • reports
        • jacoco - jacocoRootReport - html - index.html

f:id:clash_m45:20150124161706p:plain

myprojectのソースだけがカバレッジ対象になってます!


なんつーか一番はまりやすいのはprojectのプロパティの使い方だなーとかなり思ってます。
略式記法がわかりづらすぎる・・・

はてなダイアリーからブログに移行しました。