Search code examples
androidgradlecode-coveragejacoco

Measuring tests coverage in multi-module and dynamic feature module Android project using Jacoco


I am currently developping a new Android application in which one I am trying to use the modularization principles.

My android project is composed of the folllowing modules:

  • app of type com.android.application
  • core of type com.android.library
  • home of type com.android.dynamic-feature

I would like to produce an unified code coverage report for the CI (Azure Pipeline) in which one I can see the global code coverage of unit tests and instrumented tests for all the modules.

I read so many articles and tried several custom jacoco gradle plugins in order to produce a such report with no success.

I finally wrote the following jacoco.gradle file:

apply plugin: 'jacoco'

tasks.withType(Test) {
  jacoco.includeNoLocationClasses = true
  jacoco.excludes = ['jdk.internal.*']
}

project.afterEvaluate {
}
tasks.create(name: "createUnifiedCoverageReport", type: JacocoReport, dependsOn: [
':app:testInternalDebugUnitTest', ':app:createInternalDebugCoverageReport',
':home:testInternalDebugUnitTest', ':home:createInternalDebugCoverageReport',
':core:testDebugUnitTest', ':core:createDebugCoverageReport',
]) {

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

  def fileFilter = [ 
    //some files to filter
  ]

  classDirectories.setFrom(files([
  //app
  fileTree(dir: "$project.rootDir/app/build/tmp/kotlin-classes/internalDebug", excludes: fileFilter),
  fileTree(dir: "$project.rootDir/app/build/intermediates/javac/internalDebug", excludes: fileFilter),

  //home
  fileTree(dir: "$project.rootDir/home/build/tmp/kotlin-classes/debug", excludes: fileFilter),
  fileTree(dir: "$project.rootDir/home/build/intermediates/javac/debug", excludes: fileFilter),

  //core
  fileTree(dir: "$project.rootDir/core/build/tmp/kotlin-classes/debug", excludes: fileFilter),
  fileTree(dir: "$project.rootDir/core/build/intermediates/javac/debug", excludes: fileFilter),
  ]))

  def coverageSourceDirs = [
  //app
  "$project.rootDir/app/src/main/java",
  "$project.rootDir/app/src/main/kotlin",

  //home
  "$project.rootDir/home/src/main/java",
  "$project.rootDir/home/src/main/kotlin",

  //core
  "$project.rootDir/core/src/main/java",
  "$project.rootDir/core/src/main/kotlin",
  ]

  additionalSourceDirs.setFrom(files(coverageSourceDirs))
  sourceDirectories.setFrom(files(coverageSourceDirs))

  def uiAppTestsData = fileTree(dir: "${project.rootDir}/app/build/outputs/code_coverage/internalDebugAndroidTest/connected/", includes: ["**/*.ec"])
  def uiHomeTestsData = fileTree(dir: "${project.rootDir}/home/build/outputs/code_coverage/internalDebugAndroidTest/connected/", includes: ["**/*.ec"])
  def uiCoreTestsData = fileTree(dir: "${project.rootDir}/core/build/outputs/code_coverage/debugAndroidTest/connected/", includes: ["**/*.ec"])

  executionData(files([
  //app
  uiAppTestsData,
  "$project.rootDir/app/build/jacoco/testInternalDebugUnitTest.exec",

  //home
  uiHomeTestsData,
  "$project.rootDir/home/build/jacoco/testInternalDebugUnitTest.exec",

  //core
  uiCoreTestsData,
  "$project.rootDir/core/build/jacoco/testDebugUnitTest.exec",
  ]))
}

This jacoco.gradle is then applied in each module with the following instruction:

apply from: '../jacoco.gradle'

So, in order to create the unified code coverage report, I use the gradle task createUnifiedCoverageReport.

The task create an unified code coverage report that contains the classe of the app and the core modules. But the classes from the home module is missing from this report.

So I have several questions:

  • how to modify my Jacoco configuration in order to produce an unified code coverage report that includes unit tests and instrumented tests for the 3 modules?
  • Is it possible to do that with the "pure" jacoco gradle plugin or should I use a more Android oriented one?

Thank you for your help.


Solution

  • The configuration was OK but the path of the classes for the module was incorrect. The flavor information was missing :

    fileTree(dir: "$project.rootDir/home/build/tmp/kotlin-classes/internalDebug", excludes: fileFilter),
    fileTree(dir: "$project.rootDir/home/build/intermediates/javac/internalDebug", excludes: fileFilter),