I wanted to generate code coverage reports on my JUnit tests in my android project so I added the JaCoCo gradle plugin. This is my project level build.gradle file:
apply plugin: 'jacoco'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.0.0-beta6'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
allprojects {
repositories {
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
subprojects { prj ->
apply plugin: 'jacoco'
jacoco {
toolVersion '0.7.6.201602180812'
}
task jacocoReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
group = 'Reporting'
description = 'Generate Jacoco coverage reports after running tests.'
reports {
xml {
enabled = true
destination "${prj.buildDir}/reports/jacoco/jacoco.xml"
}
html {
enabled = true
destination "${prj.buildDir}/reports/jacoco"
}
}
classDirectories = fileTree(
dir: 'build/intermediates/classes/debug',
excludes: [
'**/R*.class',
'**/BuildConfig*',
'**/*$$*'
]
)
sourceDirectories = files('src/main/java')
executionData = files('build/jacoco/testDebugUnitTest.exec')
doFirst {
files('build/intermediates/classes/debug').getFiles().each { file ->
if (file.name.contains('$$')) {
file.renameTo(file.path.replace('$$', '$'))
}
}
}
}
}
jacoco {
toolVersion '0.7.6.201602180812'
}
task jacocoFullReport(type: JacocoReport, group: 'Coverage reports') {
group = 'Reporting'
description = 'Generates an aggregate report from all subprojects'
//noinspection GrUnresolvedAccess
dependsOn(subprojects.jacocoReport)
additionalSourceDirs = project.files(subprojects.jacocoReport.sourceDirectories)
sourceDirectories = project.files(subprojects.jacocoReport.sourceDirectories)
classDirectories = project.files(subprojects.jacocoReport.classDirectories)
executionData = project.files(subprojects.jacocoReport.executionData)
reports {
xml {
enabled = true
destination "${buildDir}/reports/jacoco/full/jacoco.xml"
}
html {
enabled = true
destination "${buildDir}/reports/jacoco/full"
}
}
doFirst {
//noinspection GroovyAssignabilityCheck
executionData = files(executionData.findAll { it.exists() })
}
}
It works great by running ./gradlew jacocoFullReport. But unfortunately coverage is not reported for the tests that are run with the RobolectricTestRunner (instructions that are obviously called in the tests are not reported as covered). Tests with no #RunWith annotation or run with MockitoJUnitTestRunner report coverage just fine.
Any help would be appreciated to fix this problem.
Update 1: I noticed that I should be using the RobolectricGradleTestRunner. But it didn't help.
It is known issue with the possible workaround - https://github.com/jacoco/jacoco/pull/288
Or downgrade jacoco version to 0.7.1.201405082137
UPDATE
The workaround is not needed anymore. You must use gradle version 2.13 and jacoco version 0.7.6.201602180812.
Update root build.gradle:
buildscript {
dependencies {
classpath 'org.jacoco:org.jacoco.core:0.7.6.201602180812'
}
}
task wrapper( type: Wrapper ) {
gradleVersion = '2.13'
}
Run ./gradlew wrapper
Update project build.gradle:
apply plugin: 'jacoco'
android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
}
The accepted answer is a bit dated. Here is a similar fix we just implemented. In the module (i.e. app) build.gradle add:
apply plugin: 'jacoco'
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
This does require JaCoCo 7.6+, but you are likely using it already.
Notes for Studio:
This only fixes the CLI. If you run coverage from Studio using JaCoCo, the Robolectric coverage is still not reported. The default IntelliJ Coverage Runner seems to work fine.
The test were crashing intermittently in Studio unless I added -noverify to the Android JUnit -> VM Options
I was facing the same issue but now it is resolved for me by following this link,
issue link: https://github.com/robolectric/robolectric/issues/2230
Solution for this problem is mentioned here:
https://github.com/dampcake/Robolectric-JaCoCo-Sample/commit/f9884b96ba5e456cddb3d4d2df277065bb26f1d3
I had the same issue. I changed the jacoco plugin version and added includenolocationclasses property. Here is the working jacoco.gradle file (I am using gradle wrapper 2.14.1):
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.6.201602180812"
}
android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
}
project.afterEvaluate {
// Grab all build types and product flavors
def buildTypes = android.buildTypes.collect { type -> type.name }
def productFlavors = android.productFlavors.collect { flavor -> flavor.name }
println(buildTypes)
println(productFlavors)
// 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"
println("SourceName:${sourceName}")
println("SourcePath:${sourcePath}")
println("testTaskName:${testTaskName}")
// 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."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
def coverageSourceDirs = [
"src/main/java",
"src/$productFlavorName/java",
"src/$buildTypeName/java"
]
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
println("${project.buildDir}/jacoco/${testTaskName}.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
}
}
}
This is an old issue, but for those who are still facing, it is worth mentioning that if you are setting up JaCoCo + Robolectric + Espresso - you will indeed be using includeNoLocationClasses, but still will see this error with Java9+ and probably end up here. Add the below snippet to your module build.gradle file
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
change coverage runner to jacoco in android studio
1- select app(root of the project)
2 click on menu (run --> Edit configurations --> code coverage --> choose JaCoCo).
Related
I've been trying to exclude certain classes and/or packages from the coverage report generated by jacoco. I've worked with jacoco before but it wasn't a multi-module project, so I tried translating the configuration and export it to the modules, but so far the report comes back completely ignoring my exclusion path.
I tried many solutions like:
Filter JaCoCo coverage reports with Gradle
https://gist.github.com/aalmiray/e6f54aa4b3803be0bcac
Unable to Exclude generated classes from kotlin jacoco test coverage
https://github.com/th-deng/jacoco-on-gradle-sample/blob/master/build.gradle.kts
https://github.com/gradle/kotlin-dsl-samples/issues/1176#issuecomment-435816812 <- which is the latest tried I made.
I don't know what I'm doing wrong, I'm not very knowledgable on gradle and almost every documentation/example I find uses a single module project.
I created a small project and pushed to gitlab so you can have a better look.
The desired result would be to be able to add more paths or classes to modules directly.
I would really appreciate any help you can provide.
Code snippets:
Root build.gradle.kts
plugins {
kotlin("jvm") version "1.3.71"
jacoco
}
repositories {
mavenCentral()
}
defaultTasks("clean", "build", "test")
allprojects {
apply(plugin = "org.jetbrains.kotlin.jvm")
apply(plugin = "jacoco")
group = "org.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
jacoco {
toolVersion = "0.8.5"
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
testImplementation("org.testng:testng:6.14.3")
}
configure<JavaPluginConvention> {
sourceCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile::class) {
kotlinOptions {
jvmTarget = "1.8"
}
}
tasks.test {
useTestNG()
finalizedBy("jacocoTestReport")
}
}
tasks.jacocoTestReport {
val sourceDirs = sourceSets.main.get().allSource.srcDirs
additionalSourceDirs.setFrom(sourceDirs)
sourceDirectories.setFrom(sourceDirs)
classDirectories.setFrom(sourceSets.main.get().output)
reports {
html.isEnabled = true
xml.isEnabled = false
csv.isEnabled = false
}
}
tasks.register<JacocoReport>("jacocoRootReport") {
val sourceDirs = subprojects.map { project -> project.sourceSets.main.get().allSource.srcDirs }
dependsOn(subprojects.map { project -> project.tasks.test })
sourceDirectories.setFrom(sourceDirs)
additionalSourceDirs.setFrom(sourceDirs)
classDirectories.setFrom(subprojects.map { project -> project.sourceSets.main.get().output })
executionData.setFrom(subprojects.map { project -> project.tasks.jacocoTestReport.get().executionData })
reports {
html.isEnabled = true
xml.isEnabled = false
csv.isEnabled = false
}
}
tasks.test {
finalizedBy("jacocoRootReport")
}
Module build.gradle.kts
dependencies {
implementation(project(":api"))
}
tasks {
getByName<JacocoReport>("jacocoTestReport") {
afterEvaluate {
classDirectories.setFrom(files(classDirectories.files.map {
fileTree(it) {
exclude("org/example/AppClass.class")
}
}))
}
}
}
Also, any feedback regarding anything else is highly appreciate it, I'm trying to learn/improve and could use some :).
[Edit #1] Just added a singlemodule branch to the same repo in which is working as intended, but in a single module mode.
[Edit #2] Note I'm using jacoco: 0.8.5 gradle: 6.1 with the configuration files on Kotlin DSL (build.gradle.kts)
Some time ago I also was stuck on the process of setting up JaCoCo with Sonarqube and came up with this article which might help you.
Here's the full content of jacoco.gradle file:
apply plugin: 'jacoco'
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*'] // see related issue https://github.com/gradle/gradle/issues/5184#issuecomment-457865951
}
tasks.withType(Test) { jacoco.includeNoLocationClasses = true }
project.afterEvaluate {
(android.hasProperty('applicationVariants')
? android.'applicationVariants'
: android.'libraryVariants').all { variant ->
def variantName = variant.name
def unitTestTask = "test${variantName.capitalize()}UnitTest"
def uiTestCoverageTask = "create${variantName.capitalize()}CoverageReport"
tasks.create(name: "${unitTestTask}Coverage", type: JacocoReport, dependsOn: [
"$unitTestTask",
"$uiTestCoverageTask",
":module1:testDebugUnitTest",
":module2:testDebugUnitTest",
":moduleN:testDebugUnitTest",
]) {
group = "Reporting"
description = "Generate Jacoco coverage reports for the ${variantName.capitalize()} build"
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
def fileFilter = [
'**/R.class',
'**/R$*.class',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'**/com/example/databinding/*',
'**/com/example/generated/callback/*',
'**/android/databinding/*',
'**/androidx/databinding/*',
'**/di/module/*',
'**/*MapperImpl*.*',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/*Component*.*',
'**/*BR*.*',
'**/Manifest*.*',
'**/*$Lambda$*.*',
'**/*Companion*.*',
'**/*Module.*', /* filtering Dagger modules classes */
'**/*Dagger*.*',/* filtering Dagger-generated classes */
'**/*MembersInjector*.*',
'**/*_Factory*.*',
'**/*_Provide*Factory*.*',
'**/*Extensions*.*',
'**/*$Result.*', /* filtering `sealed` and `data` classes */
'**/*$Result$*.*'/* filtering `sealed` and `data` classes */
'**/*Args*.*', /* filtering Navigation Component generated classes */
'**/*Directions*.*' /* filtering Navigation Component generated classes */
]
classDirectories.setFrom(files([
fileTree(dir: "${buildDir}/tmp/kotlin-classes/${variantName}", excludes: fileFilter),
fileTree(dir: "$project.rootDir/module1/build/tmp/kotlin-classes/debug", excludes: fileFilter),
fileTree(dir: "$project.rootDir/module2/build/tmp/kotlin-classes/debug", excludes: fileFilter),
fileTree(dir: "$project.rootDir/moduleN/build/tmp/kotlin-classes/debug", excludes: fileFilter),
]))
def coverageSourceDirs = [
"$project.rootDir/app/src/main/java",
"$project.projectDir/src/${variantName}/java",
"$project.rootDir/module1/src/main/java",
"$project.rootDir/module2/src/main/java",
"$project.rootDir/moduleN/src/main/java"
]
additionalSourceDirs.setFrom(files(coverageSourceDirs))
sourceDirectories.setFrom(files(coverageSourceDirs))
def uiTestsData = fileTree(dir: "${buildDir}/outputs/code_coverage/${variantName}AndroidTest/connected/", includes: ["**/*.ec"])
executionData(files([
"$project.buildDir/jacoco/${unitTestTask}.exec",
uiTestsData,
"$project.rootDir/module1/build/jacoco/testDebugUnitTest.exec",
"$project.rootDir/module2/build/jacoco/testDebugUnitTest.exec",
"$project.rootDir/moduleN/build/jacoco/testDebugUnitTest.exec"
]))
}
}
}
You can examine fileFilter for further filtering options.
Then inside each module you have to import the jacoco.gradle as such:
apply from: "jacoco.gradle"
I am trying to add JaCoCo to my Android for Sonar Qube coverage. But upon running the commands ./gradlew jacocoTestReport I get the following error.
Task :app:testAlphaReleaseUnitTest FAILED
FAILURE: Build failed with an exception.
What went wrong:
Could not resolve all files for configuration ':app:jacocoAgent'.
> Could not find org.jacoco:org.jacoco.agent:org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.
Searched in the following locations:
- file:/Users/atif/Library/Android/sdk/extras/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- file:/Users/atif/Library/Android/sdk/extras/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- file:/Users/atif/Library/Android/sdk/extras/google/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- file:/Users/atif/Library/Android/sdk/extras/google/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- file:/Users/atif/Library/Android/sdk/extras/android/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- file:/Users/atif/Library/Android/sdk/extras/android/m2repository/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://dl.google.com/dl/android/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://dl.google.com/dl/android/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://maven.fabric.io/public/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://maven.fabric.io/public/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://jcenter.bintray.com/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://maven.fabric.io/public/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://maven.fabric.io/public/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://dl.google.com/dl/android/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://dl.google.com/dl/android/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
- https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.pom
- https://repo.maven.apache.org/maven2/org/jacoco/org.jacoco.agent/org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated/org.jacoco.agent-org.gradle.testing.jacoco.plugins.JacocoPluginExtension_Decorated.421021a4
Required by:
project :app
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
Here are my gradle files:
project build.gradle
buildscript{
dependencies {
classpath "org.jacoco:org.jacoco.core:0.8.2"
classpath "org.jacoco:org.jacoco.agent:0.8.2"
}
}
plugins {
id "jacoco"
}
App build.gradle:
plugins {
id "com.android.application"
id "jacoco-android"
id "org.sonarqube" version "2.6"
}
android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
android.testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
jacoco {
toolVersion = "0.8.2"
reportsDir = file("$buildDir/reports/jacoco/")
}
jacocoAndroidUnitTestReport {
csv.enabled false
html.enabled false
xml.enabled true
}
the one is a Gradle plugin, which belongs into the root project's buildscript dependencies:
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath "com.dicedmelon.gradle:jacoco-android:0.1.4"
classpath "com.android.tools.build:gradle:3.2.1"
classpath "org.jacoco:org.jacoco.core:0.8.3"
...
}
}
and the other one is a library, which belongs into the module's Java dependencies:
apply plugin: "com.android.application"
// apply plugin "jacoco"
apply plugin: "jacoco-android"
...
dependencies {
...
testImplementation "org.jacoco:org.jacoco.agent:0.8.3"
}
the jacoco-android GitHub says nothing about apply plugin "jacoco", but it might be required.
You are not defining a repository in your build. For many people that would be Maven Central.
repositories {
mavenCentral()
}
It seems as if you want to manage your libraries yourself as you are pointing to the lib folder. I assume these libraries are checked in with your source code? If the same strategy should apply to the JaCoCo libraries then you will need to put them there and assign them to the configurations of the JaCoCo plugin.
Mohammed here is the code I am using with the regular gradle jacoco plugin
its even setup to gather both espresso and spoon stuff by default
jacoco {
toolVersion = "0.8.2"
// Custom reports directory can be specfied like this:
reportsDir = file("$buildDir/outputs/reports/jacoco")
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
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]))
//sourceDirectories = files([mainSrc])
//classDirectories = files([debugTree])
getClassDirectories().setFrom(files([debugTree]))
getExecutionData().setFrom(fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec",
"**/io.gitlab.fredgrott.droidkotlinkit.*/coverageExecutionData/spoonCoverage.ec",
"outputs/code-coverage/connected/*coverage.ec"
]))
}
After updating to Android Gradle plugin 3.1.0 (from 3.0.1) my JaCoCo unit test coverage configuration started producing errors in Gradle config phase:
> Configuration with name 'androidJacocoAnt' not found.
Project-level build.gradle:
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath "org.jacoco:org.jacoco.core:0.8.1"
}
Module-level build.gradle:
apply plugin: 'jacoco'
...
android {
buildTypes {
debug {
testCoverageEnabled true
}
}
...
}
task jacocoTestReport(type: JacocoReport) {
dependsOn 'createDebugCoverageReport'
dependsOn 'testDebugUnitTest'
reports {
xml.enabled = true
html.enabled = true
csv.enabled = false
}
jacocoClasspath = configurations['androidJacocoAnt']
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"
print("${project.projectDir}/src/main/java")
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = fileTree(dir: "$buildDir", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code-coverage/connected/*coverage.ec"
])
}
Diffing the output of gradle dependencies with different plugin versions, it seems that
jacocoClasspath = configurations['androidJacocoAnt']
needs to change to
jacocoClasspath = configurations['jacocoAnt']
Do you need JaCoCo 8.1 in latest Android Gradle Plugin 3.1 or higher?
issue is in gradle plugin, in it version of the JaCoCo is hardcoded to 0.7.9
Workaround (step 1 from 2): Add into root build.gradle
buildscript {
ext {
jacocoVersion = project.JACOCO_VERSION
}
dependencies {
/* To confirm JaCoCo version run: $ ./gradlew buildEnvironment */
//region classpath "org.jacoco:org.jacoco.core:${jacocoVersion}"
/* Resolves issue of incorrect version use in one of jacoco/android plugin inner tasks */
classpath "org.jacoco:org.jacoco.core:${jacocoVersion}"
classpath "org.jacoco:org.jacoco.report:${jacocoVersion}"
classpath "org.jacoco:org.jacoco.agent:${jacocoVersion}"
//endregion
}
}
Workaround (step 2 from 2): Add into root build.gradle
/* Force Jacoco Agent version upgrade */
subprojects {
configurations.all {
resolutionStrategy {
eachDependency { details ->
if ('org.jacoco' == details.requested.group) {
details.useVersion "${jacocoVersion}"
}
}
}
}
}
Enjoy
What i want:
I want to match code coverage threshold to minimum value like 60% etc in Android gradle.
What i have tried
stack overflow question i have looked into
jacoco plugin gradle
Problem i am facing
My Gradle file is written like:
apply plugin: 'com.android.application'
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.6.201602180812"
reportsDir = file("$buildDir/customJacocoReportDir")
}
def coverageSourceDirs = [
'src/main/java',
]
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/jacocoHtml"
}
}
jacocoTestCoverageVerification {
violationRules {
rule {
limit {
minimum = 0.5
}
}
rule {
enabled = false
element = 'CLASS'
includes = ['org.gradle.*']
limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 0.3
}
}
}
}
Now, When i sync my gradle file i face following erros
Could not find method jacocoTestReport() for arguments [build_5t0t9b9hth6zfihsyl5q2obv8$_run_closure2#41a69b20] on project ':app' of type org.gradle.api.Project.
and if i comment jacocoTestReport task then
Could not find method jacocoTestCoverageVerification() for arguments [build_5t0t9b9hth6zfihsyl5q2obv8$_run_closure2#3da790a8] on project ':app' of type org.gradle.api.Project.
I am not able to understand what exactly going on here. Why jacocoTestCoverageVerification method not in plugin. What i am doing wrong.
Is it something gradle is picking jacoco plugin from android plugin?
I have tried mentioning version of jacoco to 0.6.3 as mentioned in there doc that jacocoTestCoverageVerification method is written above this version.
It'll be very helpful if anybody can sort out this problem.
Let me know in any other info required.
Thanks
This problem keeps haunting me for a few times, every time the keyword android jacocoTestCoverageVerification leads me to this page and gets no answer. Finally, I succeeded make the jacoco work and I'd like to share my solution here.
The reason gradle can't find jacocoTestCoverageVerification and jacocoTestReport is because For projects that also apply the Java Plugin, the JaCoCo plugin automatically adds the following tasks jacocoTestReport and jacocoTestCoverageVerification.
It means for projects that not appling the java Plugin, it will not add jacocoTestReport and jacocoTestCoverageVerification.
So we have to add them yourself.
Follow the link: Code Coverage for Android Testing, we can add the task jacocoTestReport.
And the same way, we also can add the task jacocoTestCoverageVerification.
Full function goes like
// https://engineering.rallyhealth.com/android/code-coverage/testing/2018/06/04/android-code-coverage.html
ext.enableJacoco = { Project project, String variant ->
project.plugins.apply('jacoco')
final capVariant = variant.capitalize()
StringBuilder folderSb = new StringBuilder(variant.length() + 1)
for (int i = 0; i < variant.length(); i++) {
char c = variant.charAt(i)
if (Character.isUpperCase(c)) {
folderSb.append('/')
folderSb.append(Character.toLowerCase(c))
} else {
folderSb.append(c)
}
}
final folder = folderSb.toString()
project.android {
buildTypes {
debug {
testCoverageEnabled true
}
}
testOptions {
unitTests.all {
jacoco {
//You may encounter an issue while getting test coverage for Robolectric tests.
//To include Robolectric tests in the Jacoco report, one will need to set the includeNolocationClasses flag to true.
// This can no longer be configured using the android DSL block, thus we search all tasks of Test type and enable it
includeNoLocationClasses = true
}
}
}
jacoco {
version = '0.8.1'
}
}
project.jacoco {
toolVersion = '0.8.1'
}
project.tasks.create(
name: 'jacocoTestCoverageVerification',
type: JacocoCoverageVerification,
dependsOn: ["test${capVariant}UnitTest",
"create${capVariant}CoverageReport"
]
) {
onlyIf = {
true
}
violationRules {
rule {
limit {
minimum = 0.5
}
}
rule {
enabled = false
element = 'CLASS'
includes = ['org.gradle.*']
limit {
counter = 'LINE'
value = 'TOTALCOUNT'
maximum = 0.3
}
}
}
def coverageSourceDirs = [
"src/main/java",
"src/main/kotlin"
]
def fileFilter = [
'**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/*_MembersInjector.class',
'**/Dagger*Component.class',
'**/Dagger*Component$Builder.class',
'**/*Module_*Factory.class',
'**/*_MembersInjector.class',
'**/Dagger*Subcomponent*.class',
'**/*Subcomponent$Builder.class',
'**/Manifest*.*'
]
def javaClasses = fileTree(
dir: "${project.buildDir}/intermediates/javac/$folder",
excludes: fileFilter
)
def kotlinClasses = fileTree(
dir: "${project.buildDir}/tmp/kotlin-classes/$variant",
excludes: fileFilter
)
group = "Reporting"
description = "Applying Jacoco coverage verification for the ${project.name} with the " +
"$variant variant."
classDirectories = files([javaClasses], [kotlinClasses])
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = fileTree(dir: "${project.buildDir}", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code_coverage/debugAndroidTest/connected/*.ec",
"outputs/code_coverage/connected/*.ec" //Check this path or update to relevant path
])
}
project.tasks.create(
name: 'jacocoTestReport',
type: JacocoReport,
dependsOn: ["test${capVariant}UnitTest",
"create${capVariant}CoverageReport"
]
) {
def coverageSourceDirs = [
"src/main/java",
"src/main/kotlin"
]
def fileFilter = [
'**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/*_MembersInjector.class',
'**/Dagger*Component.class',
'**/Dagger*Component$Builder.class',
'**/*Module_*Factory.class',
'**/*_MembersInjector.class',
'**/Dagger*Subcomponent*.class',
'**/*Subcomponent$Builder.class',
'**/Manifest*.*'
]
def javaClasses = fileTree(
dir: "${project.buildDir}/intermediates/javac/$folder",
excludes: fileFilter
)
def kotlinClasses = fileTree(
dir: "${project.buildDir}/tmp/kotlin-classes/$variant",
excludes: fileFilter
)
group = "Reporting"
description = "Generate Jacoco coverage reports for the ${project.name} with the " +
"$variant variant."
classDirectories = files([javaClasses], [kotlinClasses])
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = fileTree(dir: "${project.buildDir}", includes: [
"jacoco/testDebugUnitTest.exec",
"outputs/code_coverage/debugAndroidTest/connected/*.ec",
"outputs/code_coverage/connected/*.ec" //Check this path or update to relevant path
])
onlyIf = {
true
}
println project
println "current $project buildDir: $buildDir project buildDir: ${project.buildDir}"
System.out.flush()
reports {
html.enabled = true
html.destination file("reporting/jacocohtml")
}
}
}
Gist version
First of all check you gradle verison:
For projects that also apply the Java Plugin, The JaCoCo plugin automatically adds the following tasks:
gradle 3.3 (jacocoTestReport task)
https://docs.gradle.org/3.3/userguide/jacoco_plugin.html#sec:jacoco_tasks
gradle 3.5 (jacocoTestReport , jacocoTestCoverageVerification task)
https://docs.gradle.org/current/userguide/jacoco_plugin.html#sec:jacoco_tasks
Maybe for earlier versions of gradle you need add jacoco dependencies:
...
dependencies {
сlasspath (
[group: 'org.jacoco', name: 'org.jacoco.agent', version: version: '0.7.7.201606060606'],
[group: 'org.jacoco', name: 'org.jacoco.ant', version: version: '0.7.7.201606060606']
)}
...
I have a question that my jacoco can cover androidTest directory not test directory.How can I do make jacoco also covered test directory.
This is my gradle
apply plugin: "jacoco"
buildTypes {
debug{
testCoverageEnabled true
}
}
jacoco {
toolVersion = "0.7.6.201602180812"
reportsDir = file("$buildDir/customJacocoReportDir")}
When I run gradlew createDebugCoverageReport. It worked. But only AndroidTest covered . Junit not. In project of Android Studio, only androidTest directory covered,test directory not covered.
Here is my jacoco setup which covers not only androidTest directory and also able to work with different flavors.
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.7.201606060606"
}
project.afterEvaluate {
// 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 ->
// If buildtype and flavor combo is not available, skip
if (ignoreBuildFlavor(productFlavorName, buildTypeName)) {
return
}
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."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/classes/${sourcePath}",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*$Lambda$*.*', // Jacoco can not handle several "$" in class name.
'**/*$inlined$*.*', // Kotlin specific, Jacoco can not handle several "$" in class name.
'**/*Module.*', // Modules for Dagger.
'**/*Dagger*.*', // Dagger auto-generated code.
'**/*MembersInjector*.*', // Dagger auto-generated code.
'**/*_Provide*Factory*.*',// Dagger auto-generated code.
'**/*$Icepick*.*']
)
def coverageSourceDirs = [
"src/main/java",
"src/$productFlavorName/java",
"src/$buildTypeName/java"
]
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
}
}
And don't forget to add this line to your build.gradle:
apply from: "jacoco.gradle"
Finally,I used android-junit-jacoco-plugin.This plugin solved my problem.But if you use this plugin,the unit test have to be all success.If not,The coverage report will not be created by jacoco.