Jacoco Coverage in Multi-Module Gradle Project Not Including Sub

ghz 9months ago ⋅ 94 views

I am developing a multi-module Gradle (8.7) project, structured such that one module acts as the main application, while the others are libraries used by this application. All unit tests are located within the application module. Although these tests cover code paths in the library modules, Jacoco does not seem to generate coverage reports for these submodules, leading to inaccurately low coverage metrics. Here is the structure:

repository-root/
└── application/
    ├── app/
    │   ├── src/
    │   │   ├── main/
    │   │   │   └── java/
    │   │   └── test/
    │   └── build.gradle.kts
    ├── common/
    │   ├── src/
    │   │   └── main/
    │   │       └── java/
    │   └── build.gradle.kts
    └── build.gradle.kts

Issue: Main Problem: Despite having tests that cover library module code, Jacoco's reports only reflect coverage for the application module. Goal: Achieve a coverage report that accurately includes data from all modules, reflecting the true coverage achieved through tests run in the application module.

I tried to include coverage for one of the library modules (:common) by configuring the jacocoTestReport task in app/build.gradle.kts in the application module's build.gradle as follows:

tasks.jacocoTestReport {
    val common = project(":common")
    dependsOn(tasks.test)
    dependsOn(common.tasks.test)
    classDirectories.setFrom(
        files("$buildDir/classes/java/main"),
        files("${common.buildDir}/classes/java/main"))
    sourceDirectories.setFrom(
        files("${projectDir}src/main/java"),
        files("${common.projectDir}/src/main/java"))
    executionData.setFrom(
        files("$buildDir/jacoco/test.exec"),
        files("${common.buildDir}/jacoco/test.exec"))
    reports {
        xml.required.set(true)
        csv.required.set(false)
        html.required.set(true)
    }
}

I want to specify that I found this solution on some forum, but no test.exec is generated in my common module. So also other solutions like generate an aggregated RootReport that I found on other question maybe won't be suitable for my case.

Answers

It seems like your configuration is on the right track, but there might be some issues with the paths or dependencies. Let's revise and adjust your configuration to ensure that Jacoco generates coverage reports for all modules correctly.

First, ensure that the jacocoTestReport task is properly configured in the build.gradle.kts file of your application module:

tasks.jacocoTestReport {
    dependsOn(tasks.test)

    // Iterate over all subprojects to include their coverage data
    project.subprojects { subproject ->
        executionData.setFrom(files("${subproject.buildDir}/jacoco/test.exec"))
        classDirectories.setFrom(files("${subproject.buildDir}/classes/java/main"))
        sourceDirectories.setFrom(files("${subproject.projectDir}/src/main/java"))
    }

    reports {
        xml.required.set(true)
        html.required.set(true)
    }
}

In this configuration:

  • We use project.subprojects to iterate over all subprojects (modules).
  • For each subproject, we set the appropriate executionData, classDirectories, and sourceDirectories.
  • We ensure that both XML and HTML reports are generated.

Make sure that your tests are actually generating Jacoco coverage data (test.exec files) in each submodule. If not, you may need to review your test configurations and ensure that Jacoco is properly instrumenting your code during the tests.

Additionally, ensure that all subprojects are correctly declared in the settings.gradle.kts file in the root directory:

rootProject.name = "repository-root"

include(":application")
include(":common")

With these adjustments, Jacoco should generate accurate coverage reports for all modules in your multi-module project. If you encounter any further issues, ensure to check the Jacoco output and logs for any errors or warnings that may indicate the source of the problem.