I am trying to add Jacoco to my project for test coverage reporting.
Referred the following blogs for that,
- https://medium.com/nerd-for-tech/setup-jacoco-code-coverage-with-your-multimodule-android-app-kotlin-a0f82573a1
- https://azizbekian.medium.com/setup-jacoco-sonarqube-in-multimodule-multiflavor-kotlin-android-project-d8e7b27aed36
- https://medium.com/jamf-engineering/android-kotlin-code-coverage-with-jacoco-sonar-and-gradle-plugin-6-x-3933ed503a6e
Most of them have the same code for file filtering, but it is not working for me.
jacoco.gradle
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.7"
}
project.afterEvaluate { project ->
setupAndroidReporting()
}
def setupAndroidReporting() {
tasks.withType(Test) {
// Whether or not classes without source location should be instrumented
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
// Grab all build types and product flavors
def buildTypes = android.buildTypes.collect { type ->
type.name
}
def productFlavors = android.productFlavors.collect { flavor ->
flavor.name
}
// When no product flavors defined, use empty
if (!productFlavors) {
productFlavors.add('')
}
productFlavors.each { productFlavorName ->
buildTypes.each { buildTypeName ->
def sourceName, sourcePath
if (!productFlavorName) {
sourceName = sourcePath = "${buildTypeName}"
} else {
sourceName = "${productFlavorName}${buildTypeName.capitalize()}"
sourcePath = "${productFlavorName}/${buildTypeName}"
}
def testTaskName = "test${sourceName.capitalize()}UnitTest"
// Create coverage task of form 'testFlavorTypeCoverage' depending on 'testFlavorTypeUnitTest'
task "${testTaskName}Coverage"(type: JacocoReport, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Generate Jacoco coverage reports on the ${sourceName.capitalize()} build."
def fileFilter = [
// data binding
'android/databinding/**/*.class',
'**/android/databinding/*Binding.class',
'**/android/databinding/*',
'**/androidx/databinding/*',
'**/BR.*',
// android
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*',
// kotlin
'**/*MapperImpl*.*',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/*Component*.*',
'**/*BR*.*',
'**/Manifest*.*',
'**/*$Lambda$*.*',
'**/*Companion*.*',
'**/*Module*.*',
'**/*Dagger*.*',
'**/*Hilt*.*',
'**/*MembersInjector*.*',
'**/*_MembersInjector.class',
'**/*_Factory*.*',
'**/*_Provide*Factory*.*',
'**/*Extensions*.*',
// sealed and data classes
'**/*$Result.*',
'**/*$Result$*.*',
// adapters generated by moshi
'**/*JsonAdapter.*',
// Hilt
'**/*Module.kt',
'**/di/**',
'dagger.hilt.internal/*',
'hilt_aggregated_deps/*',
]
def javaTree = fileTree(dir: "${project.buildDir}/intermediates/javac/$sourceName/classes", exclude: fileFilter)
def kotlinTree = fileTree(dir: "${project.buildDir}/tmp/kotlin-classes/$sourceName", exclude: fileFilter)
classDirectories.from = files([javaTree], [kotlinTree])
executionData.from = files("${project.buildDir}/jacoco/${testTaskName}.exec")
def coverageSourceDirs = [
"src/main/java",
"src/$productFlavorName/java",
"src/$buildTypeName/java",
]
sourceDirectories.setFrom(files(coverageSourceDirs))
additionalSourceDirs.setFrom(files(coverageSourceDirs))
reports {
csv.enabled false // change if needed
xml.enabled false // change if needed
html {
enabled true
destination file("${buildDir}/coverage-report")
}
}
}
}
}
System.out.println("Test coverage report: ${buildDir}/reports/coverage/androidTest/debug/index.html")
}
// Jacoco
configurations.all {
resolutionStrategy {
eachDependency { details ->
if ('org.jacoco' == details.requested.group) {
details.useVersion "0.8.7"
}
}
}
}
The generated report still includes files from these hilt directories.
Note:
Also noticed it is not running/reporting any tests as the coverage is shown as 0%.
P.S: Please add a comment if any required info is missing in the question.

Please check
additionalClassDirscontent. If it is not empty, try clearing it withclassDirectories.setFrom(files()). This should do the trick.The coverage task does not have dependencies on
testorconnectedTest, so you have to run the tests manually before starting this task.