Reputation: 407
We've integrated JaCoCo plugin to our Android project for measuring the code coverage via SonarQube. However, it could not be measured. Because SonarQube expects coverage report as an XML format. We add a custom jacocoTestReport task to build.gradle(module). It generates an xml file but it does not contain any coverage information. Have you encountered such a problem? If you had, do you have any solution?
build.gradle(root)
classpath "org.jacoco:org.jacoco.core:0.8.5"
build.gradle(module)
apply plugin: 'jacoco'
...
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
reports {
xml.enabled = true
html.enabled = true
}
def fileFilter = ['**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
getSourceDirectories().setFrom(files([mainSrc]))
getClassDirectories().setFrom(files([debugTree]))
getExecutionData().setFrom(fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code-coverage/connected/*coverage.ec"
]))
Output:
Upvotes: 0
Views: 2991
Reputation: 288
Baseline: AGP 7.1.2 + , jacoco 0.88, hilt 2.42, many you have to update the jacoco.gradle file.
apply plugin: 'jacoco'
jacoco {
toolVersion = '0.8.7'
}
task jacocoTestReport { self ->
build.dependsOn self
}
//Use this method to generate the HTML and XML coverage report files for
Unit and UI test cases
android.testVariants.all {
def variant = it.testedVariant
def variantName = variant.name.capitalize()
def unitTestTask = "test${variantName}UnitTest"
def androidTestCoverageTask = "create${variantName}CoverageReport"
tasks.create(name: "jacoco${name}TestReport", type: JacocoReport)
{ self ->
group = 'Reporting'
description = "Generates Jacoco coverage reports on the
${variant.name} variant"
reports {
xml.enabled(true)
html.enabled(true)
}
def excludes = [
// data binding
'android/databinding/**/*.class',
'**/android/databinding/*Binding.class',
'**/android/databinding/*',
'**/androidx/databinding/*',
'**/BR.*',
// android
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*',
// butterKnife
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
// dagger
'**/*_MembersInjector.class',
'**/Dagger*Component.class',
'**/Dagger*Component$Builder.class',
'**/*Module_*Factory.class',
'**/di/module/*',
'**/*_Factory*.*',
'**/*Module*.*',
'**/*Dagger*.*',
'**/*Hilt*.*',
// 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$*.*'
]
def javaClasses = fileTree(dir:
variant.javaCompileProvider.get().destinationDir,
excludes: excludes)
def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-
classes/${variant.name}",
excludes: excludes)
classDirectories.setFrom(files([
javaClasses,
kotlinClasses
]))
def variantSourceSets = "${project.projectDir}/src/main/java"
sourceDirectories.setFrom(project.files(variantSourceSets))
def androidTestsData = fileTree(dir:
"${buildDir}/outputs/code_coverage/${variant.name}AndroidTest/connected/",
includes: ["**/*.ec"])
executionData(files([
"$project.buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest/test
${variantName}UnitTest.exec",
androidTestsData
]))
dependsOn unitTestTask, androidTestCoverageTask
jacocoTestReport.dependsOn self
}
}
Due to AGP being updated below path is changed in the build directory.
//Replace this code
executionData.from = fileTree(dir: buildDir, includes: [
"jacoco/test${variantName}UnitTest.exec",
"/outputs/code_coverage/${variant.name}AndroidTest/connected/*coverage.ec"
])
//To this code
def androidTestsData = fileTree(dir: "${buildDir}/outputs/code_coverage/${variant.name}AndroidTest/connected/", includes: ["**/*.ec"])
executionData(files([
"$project.buildDir/outputs/unit_test_code_coverage/${variant.name}UnitTest/test${variantName}UnitTest.exec",
androidTestsData
]))
Upvotes: 1
Reputation: 407
After some googling, I've finally solved this problem. Source and class directories' paths were incorrect. Correct paths should be like this:
def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir,
excludes: fileFilter)
def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}",
excludes: fileFilter)
classDirectories.setFrom(files([
javaClasses,
kotlinClasses
]))
def variantSourceSets = variant.sourceSets.java.srcDirs.collect { it.path }.flatten()
sourceDirectories.setFrom(project.files(variantSourceSets))
Regards,
Upvotes: 0
Reputation: 61
What is your gradle version? It seems there are some issues with gradle 6.4+ https://github.com/gradle/gradle/issues/14132
Upvotes: 0