Related
Below is my sonarqube properties snippet:
sonarqube {
properties{
property "sonar.junit.reportPaths", "build/test-results/testDebugUnitTest/*.xml"
property("sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacocoTestReport.xml"
}
}
Jacoco configuration and properties are working fine, how did I confirm this?
I created a java class and wrote a unit test for it, sonarqube recognises this and recorded it as part of the Code Coverage, while it basically ignore all the Kotlin file test.
I went ahead to change a kotlin file to java and also write a UnitTest for it and yes, it got recorgnised and add as part of the Code Coverage, again, the kotlin file tests were ignored.
Below is my Jacoco.gradle by the way:
apply plugin: 'jacoco'
ext {
coverageExclusions = [
'**/*Activity*.*',
'**/*Fragment*.*',
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
]
}
jacoco {
toolVersion = '0.8.6'
reportsDir = file("$buildDir/reports")
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
tasks.withType(Test) {
finalizedBy jacocoTestReport // report is always generated after tests run
}
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
group = "Reporting"
description = "Generate Jacoco coverage reports for Debug build"
reports {
xml.enabled(true)
html.enabled(true)
xml.destination(file("build/reports/jacocoTestReport.xml"))
}
def debugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug/classes", excludes: coverageExclusions)
def mainSrc = "/src/main/java"
additionalSourceDirs.from = files(mainSrc)
sourceDirectories.from = files([mainSrc])
classDirectories.from = files([debugTree])
executionData.from = fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
])
After so much research and debugging, and also with info's from #LarryX answer above, I was able to play around the classes as he said, below is my working Jacoco.gradle file.
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.8.4"
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
project.afterEvaluate {
(android.hasProperty('applicationVariants')
? android.'applicationVariants'
: android.'libraryVariants')
.all { variant ->
def variantName = variant.name
def unitTestTask = "test${variantName.capitalize()}UnitTest"
def androidTestCoverageTask = "create${variantName.capitalize()}CoverageReport"
tasks.create(name: "${unitTestTask}Coverage", type: JacocoReport, dependsOn: [
"$unitTestTask",
"$androidTestCoverageTask"
]) {
group = "Reporting"
description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build"
reports {
html.enabled(true)
xml.enabled(true)
csv.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/${variantName}",
excludes: excludes)
classDirectories.setFrom(files([
javaClasses,
kotlinClasses
]))
def variantSourceSets = variant.sourceSets.java.srcDirs.collect { it.path }.flatten()
sourceDirectories.setFrom(project.files(variantSourceSets))
def androidTestsData = fileTree(dir: "${buildDir}/outputs/code_coverage/${variantName}AndroidTest/connected/", includes: ["**/*.ec"])
executionData(files([
"$project.buildDir/jacoco/${unitTestTask}.exec",
androidTestsData
]))
}
}
}
Take note of :
def javaClasses = fileTree(dir: variant.javaCompileProvider.get().destinationDir,
excludes: excludes)
def kotlinClasses = fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}",
excludes: excludes)
classDirectories.setFrom(files([
javaClasses,
kotlinClasses
]))
Also, take note of project.afterEvaluate I'm not sure this is relevant but cause I think you must have run and generated your test before this section runs anyways.
This solution also works for a library if not an actual android application.
And then my sonarqube.gradle file below.
apply plugin: "org.sonarqube"
sonarqube {
properties {
property "sonar.host.url", "http://localhost:9000/"
property "sonar.projectKey", "fair"
property "sonar.projectName", "fair"
property "sonar.login", "de9d79fe4d3aef9879567afc91a2ce465038d9be"
property "sonar.projectVersion", "${android.defaultConfig.versionName}"
property "sonar.junit.reportsPath", "build/test-results/testDebugUnitTest"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.android.lint.report", "build/reports/lint-results.xml"
property "sonar.jacoco.reportPaths", "build/jacoco/testDebugUnitTest.exec"
property "sonar.jacoco.itReportPath", fileTree(dir: project.projectDir, includes: ["**/*.ec"])
property "sonar.coverage.jacoco.xmlReportPaths", "build/reports/jacoco/testDebugUnitTestCoverage/testDebugUnitTestCoverage.xml"
}
}
tasks.sonarqube.dependsOn ":app:testDebugUnitTestCoverage"
Finally my build.gradle(project)
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32"
classpath "org.jacoco:org.jacoco.core:0.8.7"
classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3")
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Please check jacoco config for java + kotlin project:
http://vgaidarji.me/blog/2017/12/20/how-to-configure-jacoco-for-kotlin-and-java-project/
Key is to include kotlin directories in sourceDirectories and classeDirectories
I am working on the SonarQube code coverage...I have implemented locally and able to see jacocoTestReprt and all other necessary files...but when I run the task on Jenkins jacoco not able to generate any files.
In app/build.gradle
task jacocoTestReport(type: JacocoReport, dependsOn:
[‘testProdReleaseUnitTest’]) {
reports {
xml.enabled = true
html.enabled = true }
def fileFilter = ['/R.class', '/R$.class', '**/BuildConfig.',
'/Manifest*.*', 'android//.'] def debugTree = fileTree(dir:
"${buildDir}/intermediates/javac/prodRelease/classes", excludes:
fileFilter) def mainSrc = "${project.projectDir}/src/main/java"
getSourceDirectories().setFrom(files([mainSrc]))
getClassDirectories().setFrom(files([debugTree]))
getExecutionData().setFrom(fileTree(dir: "$buildDir", includes:
["jacoco/testProdReleaseUnitTest.exec"])
}
Jenkins artifacts:
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:
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
]))
What is your gradle version? It seems there are some issues with gradle 6.4+
https://github.com/gradle/gradle/issues/14132
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,
I have an Android app which is made up of 2 modules:
App - UI
Submodule - has most of the business logic
For each of them I have a gradle task to validate code coverage:
App: UI Code coverage (Espresso)
Submodule: Unit tests code coverage
As a requirement for the client, I need to merge those two reports to get the overall/global code coverage of the app.
Note: I'm using Gradle version 3.1.2.
App Gradle file:
apply plugin: 'jacoco'
android {
testBuildType "uiTest"
...
buildTypes {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
debuggable true
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
matchingFallbacks = ['debug']
}
// TESTS
// unitTest will be used to run unit tests.
unitTest.initWith(buildTypes.debug) //Beware the buildType this depends on MUST be above on the gradle file
unitTest {
applicationIdSuffix ".unitTest"
versionNameSuffix "-unitTest"
testCoverageEnabled = true
matchingFallbacks = ['unitTest', 'debug']
}
// uiTest will be used to run ui tests.
uiTest.initWith(buildTypes.debug) //Beware the buildType this depends on MUST be above on the gradle file
uiTest {
applicationIdSuffix ".uiTest"
versionNameSuffix "-uiTest"
testCoverageEnabled = true
matchingFallbacks = ['uiTest', 'debug']
}
...
SubModule Gradle file:
apply plugin: 'jacoco'
android {
testBuildType "uiTest"
buildTypes {
debug {
}
unitTest {
initWith(buildTypes.debug)
testCoverageEnabled = true
}
uiTest {
initWith(buildTypes.debug)
testCoverageEnabled = true
}
...
}
I've tried several ways, this one below indeed merges the tests.. but the coverage is not appearing correctly:
The task for creating UI Test coverage in the app:
//UI Test Coverage filtered (we need to run unit tests of App to be able to use Jacoco to filter)
task createTestReport(type: JacocoReport, dependsOn: [':app:testUnitTestUnitTest', ':app:createUiTestAndroidTestCoverageReport']) {
reports {
html.enabled = true
}
def fileFilter = [
//Android stuff
'**/R.class',
'**/BR.class',
'**/R$*.class',
'**/BR$*.class',
'**/BuildConfig.*',
'android/**/*.*',
//Data Binding
'**/*databinding',
'**/*binders',
'**/*layouts',
'**/Manifest*.*',
'**/*Test*.*',
"**/services/**/model/**",
//Utils
'**/utils/*.*',
'**/utils/**/*.*'
]
//To support Java coverage on Unit tests
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/unitTest", excludes: fileFilter)
//To support Kotlin coverage on Unit tests
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/unitTest", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
def debugSrc = "${project.projectDir}/src/debug/java"
sourceDirectories = files([mainSrc, debugSrc])
def appAndroidTests = fileTree(dir: "${buildDir}/outputs/code-coverage/connected/", includes: ["**/*.ec"])
def appOtherAndroidTests = fileTree(dir: "${buildDir}/outputs/androidTest-results/connected/", includes: ["**/*.ec"])
classDirectories = files([debugTree], [kotlinDebugTree])
executionData = files("${buildDir}/jacoco/testUnitTestUnitTest.exec", appAndroidTests, appOtherAndroidTests)
}
The task for creating Unit test coverage in the sub-module:
//Unit Test Coverage filtered
task createTestReport(type: JacocoReport, dependsOn: ['testUnitTestUnitTest']) {
reports {
html.enabled = true
}
def fileFilter = ['**/R.class',
'**/BR.class',
'**/R$*.class',
'**/BR$*.class',
'**/BuildConfig.*',
'**/*databinding/**/*.*',
'**/Manifest*.*',
'**/*Test*.*',
"**/services/**/model/**",
'android/**/*.*',
'**/utils/*.*',
'**/utils/**/*.*']
//To support Java coverage on Unit tests
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/unitTest", excludes: fileFilter)
//To support Kotlin coverage on Unit tests
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/unitTest", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
def debugSrc = "${project.projectDir}/src/debug/java"
sourceDirectories = files([mainSrc, debugSrc])
classDirectories = files([debugTree], [kotlinDebugTree])
executionData = files("${buildDir}/jacoco/testUnitTestUnitTest.exec")
}
The task for creating Global coverage in the app:
//Global Test Coverage
task createGlobalTestReport(type: JacocoReport,
dependsOn: [':app:testUnitTestUnitTest', ':app:createTestReport',
':submodule:testUnitTestUnitTest']) {
reports {
html.enabled = true
}
def fileFilter = [
//Android stuff
'**/R.class',
'**/BR.class',
'**/R$*.class',
'**/BR$*.class',
'**/BuildConfig.*',
'android/**/*.*',
//Data Binding
'**/*databinding',
'**/*binders',
'**/*layouts',
'**/Manifest*.*',
'**/*Test*.*',
"**/services/**/model/**",
//Utils
'**/utils/*.*',
'**/utils/**/*.*'
]
// Note: **/reviews/ReviewService*.* was added as BazaarVoice cannot be mocked
//To support Java coverage on Unit tests
def debugAppTree = fileTree(dir: "${buildDir}/intermediates/classes/unitTest", excludes: fileFilter)
//To support Kotlin coverage on Unit tests
def debugKotlinAppTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/unitTest", excludes: fileFilter)
def debugSdkTree = fileTree(dir: "..//build/intermediates/classes/unitTest", excludes: fileFilter)
def debugKotlinSdkTree = fileTree(dir: "../submodule/build/tmp/kotlin-classes/unitTest", excludes: fileFilter)
def mainAppSrc = "${project.projectDir}/src/main/java"
def debugAppSrc = "${project.projectDir}/src/debug/java"
def mainSdkSrc = "../submodule/src/main/java"
def debugSdkSrc = "../submodule/src/debug/java"
sourceDirectories = files([mainAppSrc, debugAppSrc,
mainSdkSrc, debugSdkSrc])
def appAndroidTests = fileTree(dir: "${buildDir}/outputs/code-coverage/connected/", includes: ["**/*.ec"])
def sdkAndroidTests = fileTree(dir: "../submodule/build/outputs/code-coverage/connected/", includes: ["**/*.ec"])
classDirectories = files([debugAppTree, debugSdkTree,
debugKotlinAppTree, debugKotlinSdkTree])
executionData = files("${buildDir}/jacoco/testUnitTestUnitTest.exec"
, "../submodule/build/jacoco/testUnitTestUnitTest.exec"
, appAndroidTests
, sdkAndroidTests
)
}
Any help would be much appreciated
I'm not sure it works with multiple build types.
Try merging it into a single build type and:
App Gradle file:
apply plugin: 'jacoco'
android {
testBuildType "automationTest"
...
buildTypes {
debug {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
debuggable true
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
matchingFallbacks = ['debug']
}
// TESTS
automationTest {
applicationIdSuffix ".automationTest"
versionNameSuffix "-automationTest"
testCoverageEnabled = true
matchingFallbacks = ['automationTest', 'debug']
}
...
SubModule Gradle file:
apply plugin: 'jacoco'
android {
buildTypes {
debug {
}
automationTest {
initWith(buildTypes.debug)
testCoverageEnabled = true
}
release {
initWith(buildTypes.debug)
}
...
}
The task for creating Unit test coverage in the sub-module:
task createUnitTestReport(type: JacocoReport, dependsOn: ['testAutomationTestUnitTest']) {
reports {
html.enabled = true
}
def fileFilter = ['**/R.class',
'**/BR.class',
'**/R$*.class',
'**/BR$*.class',
'**/BuildConfig.*',
'**/*databinding/**/*.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*']
//To support Java coverage on Unit tests
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/automationTest", excludes: fileFilter)
//To support Kotlin coverage on Unit tests
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/automationTest", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
def debugSrc = "${project.projectDir}/src/debug/java"
sourceDirectories = files([mainSrc, debugSrc])
classDirectories = files([debugTree], [kotlinDebugTree])
executionData = files("${buildDir}/jacoco/testAutomationTestUnitTest.exec")
}
The task for creating Global coverage in the app:
task createGlobalTestReport(type: JacocoReport,
dependsOn: [':app:createUiTestReport', ':submodule:createUnitTestReport']) {
reports {
html.enabled = true
}
def fileFilter = [
//Android stuff
'**/R.class',
'**/BR.class',
'**/R$*.class',
'**/BR$*.class',
'**/BuildConfig.*',
'android/**/*.*',
//Data Binding
'**/*databinding',
'**/*binders',
'**/*layouts',
'**/Manifest*.*',
'**/*Test*.*'
]
//To support Java coverage on Unit tests
def debugAppTree = fileTree(dir: "${buildDir}/intermediates/classes/automationTest", excludes: fileFilter)
//To support Kotlin coverage on Unit tests
def debugKotlinAppTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/automationTest", excludes: fileFilter)
def debugSdkTree = fileTree(dir: "../submodule/build/intermediates/classes/automationTest", excludes: fileFilter)
def debugKotlinSdkTree = fileTree(dir: "../submodule/build/tmp/kotlin-classes/automationTest", excludes: fileFilter)
def mainAppSrc = "${project.projectDir}/src/main/java"
def debugAppSrc = "${project.projectDir}/src/debug/java"
def mainSdkSrc = "../submodule/src/main/java"
def debugSdkSrc = "../submodule/src/debug/java"
sourceDirectories = files([mainAppSrc, debugAppSrc,
mainSdkSrc, debugSdkSrc])
classDirectories = files([debugAppTree, debugSdkTree,
debugKotlinAppTree, debugKotlinSdkTree])
def appAndroidTests = fileTree(dir: "${buildDir}/outputs/code-coverage/connected/", includes: ["*.ec"])
executionData = files("${buildDir}/jacoco/testAutomationTestUnitTest.exec"
, "../submodule/build/jacoco/testAutomationTestUnitTest.exec"
, appAndroidTests
)
}
I use a plugin by palantir to create aggregate coverage reports for my different modules - com.palantir.jacoco-full-report
Basically, in your root gradle file, you need to add this in your dependencies:
classpath 'com.palantir:jacoco-coverage:0.4.0'
After that, when you run ./gradlew test jacocoFullReport a test report in build/reports/jacoco/jacocoFullReport/ that evaluates the coverage yielded by all subprojects combined is created.
I can setup spoon with:
spoon {
//...
codeCoverage = true
}
to generage coverage.ec files.
How can I generate the reports in .xml or .html?
You can do this creating your own task using the built-in Jacoco Gradle plugin:
apply plugin: 'jacoco'
task jacocoTestReport(type: JacocoReport, dependsOn: ['<taskThatProducesEcFile>']) {
reports {
xml.enabled = true
html.enabled = true
}
def fileFilter = ['**/R.class', <another filters...>]
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = files([<your_path_to_ec_file>])
}