I am currently setting up Jacoco in an Android codebase that my team built. I don't have much experience in Android but I have set up Jacoco before in a Spring Boot codebase so that I can track the test coverage in Sonarqube. But I am having hard time doing in the Android codebase.
So the directory layout looks like this
MyApp/
├─ app/
│ ├─ build/
│ ├─ src/
│ ├─ build.gradle.kts
├─ buildSrc/
│ ├─ build.gradle.kts
├─ modules/
│ ├─ module1/
│ │ ├─ src/
│ │ ├─ build.gradle.kts
│ ├─ module2/
│ │ ├─ src/
│ │ ├─ build.gradle.kts
├─ build.gradle.kts
├─ gradle.properties
├─ gradlew
├─ settings.gradle.kts
I tried adding jacoco
in the MyApp/build.gradle.kts
.
plugins {
id(Dependencies.Plugins.androidApplication) version Dependencies.Versions.androidAppplication apply false
id(Dependencies.Plugins.androidLibrary) version Dependencies.Versions.androidLibrary apply false
id(Dependencies.Plugins.hilt) version Dependencies.Versions.hilt apply false
id(Dependencies.Plugins.openApi) version Dependencies.Versions.openApi
id(Dependencies.Plugins.kotlinAndroid) version Dependencies.Versions.kotlinAndroid apply false
id(Dependencies.Plugins.sonarqube) version Dependencies.Versions.sonarqube
id("jacoco")
}
I tried executing bash gradlew test jacocoTestReport
but it says
Task 'jacocoTestReport' not found in root project 'MyApp' and its subprojects.
I tried adding the line below in the MyApp/build.gradle.kts
, per JaCoco Plugin documentation (https://docs.gradle.org/current/userguide/jacoco_plugin.html)
tasks.test {
finalizedBy(tasks.jacocoTestReport) // report is always generated after tests run
}
tasks.jacocoTestReport {
dependsOn(tasks.test) // tests are required to run before generating the report
}
but the output says something like below
Script compilation errors:
Line 12: tasks.test {
^ Unresolved reference: test
Line 13: finalizedBy(tasks.jacocoTestReport)
^ Unresolved reference: finalizedBy
I did similar configuration in a Spring Boot project before which ran normal. But here it cannot even detect task jacocoTestReport
.
What did I do wrong?
In a traditional Java or Kotlin JVM project, there is a single test task, accessible via
tasks.test
.However, in an Android project, there isn't a single
test
task. Instead, there are multiple test tasks, one for each build variant. The test tasks are namedtest<Variant>UnitTest
. For example, you might havetestDebugUnitTest
andtestReleaseUnitTest
.When you try to access
tasks.test
in an Android project, it results in an "Unresolved reference: test" error because there is no task with the nametest
.Similarly,
finalizedBy(tasks.jacocoTestReport)
is a way to specify task dependencies, saying that thejacocoTestReport
task should always be run after thetest
task. However, since there is notest
task in an Android project, this results in an "Unresolved reference: finalizedBy
" error.Following "How do I add plugins to subprojects based on what plugins are present?", you might need to create a Jacoco setup in your root
build.gradle.kts
that automatically applies the correct Jacoco setup for each module that uses the Android plugin.Using a Gradle DLS script:
That script will check every subproject in the build. If a subproject applies your IDs, like
Dependencies.Plugins.androidApplication
orDependencies.Plugins.androidLibrary
plugin, it adds Jacoco tasks for that subproject.For each of these projects, add a
jacocoTestReport
task which generates the Jacoco reports.One important thing to note is that the Android Gradle plugin creates a
test
task for each build variant of the app/library module. For example, if you havedebug
andrelease
build types, the Android Gradle plugin will createtestDebugUnitTest
andtestReleaseUnitTest
tasks.Meaning, you will need to run the specific
test
task and thenjacocoTestReport
like this:That will run the unit tests for
module1
and then generate the Jacoco report. Note that if you are using product flavors, you might have more than two types oftest
tasks (e.g.,testFreeDebugUnitTest
,testPaidDebugUnitTest
, etc.), so do adjust your command accordingly to fit your project configuration.Consolidating JaCoCo coverage reports from multiple modules into a single report in a multi-module Android project means aggregating the execution data (
*.exec
files) and source information from all relevant subprojects.That would require a custom Gradle task in your root
build.gradle.kts
file that gathers data from all modules and generates a comprehensive report.In your root
build.gradle.kts
, define a task that will collect and aggregate coverage data from all subprojects. That task will depend on all individualjacocoTestReport
tasks to make sure all coverage data is available before aggregation.The example paths provided (
src/main/kotlin
,intermediates/javac/debug/classes
,jacoco/testDebugUnitTest.exec
) are based on a typical Android project structure: adapt them based on your project's configuration:src/main/java
if your project primarily uses Java.debug
withrelease
if you are generating reports for release builds).To generate the aggregated report, run the following command:
That would first cleans previous builds to make sure fresh data is used for the report, then runs the
jacocoRootReport
task, which aggregates data from all subprojects.