Merge Jacoco coverage reports for multiproject setups

I have been searching for the answer to this question several times over the last year, each time giving up after a couple of hours of testing a zillion different suggestions - in vain. I am not deeply into Gradle, so much of it seems like black magic. I finally managed to adapt Benjamin’s suggestion into something that works for me (a project with subprojects common, client, server, and integration). And it works on Gradle 5.6.

task jacocoMergeAll(type: JacocoMerge) {
   dependsOn(subprojects.test, subprojects.jacocoTestReport)
   subprojects.each { subproject ->
      // exclude common and integration subprojects
      if (subproject.name != 'integration' &&
          subproject.name != 'common') {
        executionData subproject.tasks.withType(Test)
      } 
   } 
}

task jacocoRootReport(type: JacocoReport, group: 'Coverage reports') {
  description = 'Generates an aggregate report from all subprojects'
  dependsOn(jacocoMergeAll)

  additionalSourceDirs.from =
                   files(subprojects.sourceSets.main.allSource.srcDirs)
  sourceDirectories.from =
                   files(subprojects.sourceSets.main.allSource.srcDirs)
  classDirectories.from =
                   files(subprojects.sourceSets.main.output)
  executionData.from =
                   files("${buildDir}/jacoco/jacocoMergeAll.exec")

  reports {
    html.enabled = true
    xml.enabled = false
  }
}

The one thing I could not figure out is how to automatically avoid subprojects that do not produce a jacoco.exec (which is the case of ‘common’), so I have to have a manual test in the merge task, which is a bit ‘handheld’. Anyone who knows the trick to make gradle ignore in case there is no file?

Embed the above in the root build.gradle, and gradle test jacocoRootReport to get coverage for all subprojects in the build/reports/… folder.

3 Likes