I am attempting to refine my code coverage in my project, and want to exclude any View classes (as they are not tested/testable) but include any ViewModel classes... but I can't seem to get the wildcard filters to cooperate, it's all or nothing!
def excludedPatterns = [
//... other stuff
'**/*Fragment*.*',
'**/*Activity*.*',
'**/*Adapter*.*',
'**/*View*.*', // <-- this line is excluding classes ending w/ ViewModel
'**/*ViewState*.*',
'**/*ViewHolder*.*',
]
task codeCoverageReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
html.enabled true
}
def debugTree = fileTree(
dir: "$project.buildDir/tmp/kotlin-classes/debug",
excludes: excludedPatterns
)
classDirectories.from = files([debugTree])
def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories.from = files([mainSrc])
executionData.from = fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec',
'outputs/code-coverage/connected/*coverage.ec'
])
}
I've tried several variations of **/*View*.*', including **/*View.*' and others...
Is there something that I am overlooking?
You can use filter on fileTree.
For example for
src/main/java/View.java
class View {}
src/main/java/ViewModel.java
class ViewModel {}
src/main/java/Example.java
class Example {}
src/test/java/ExampleTest.java
import org.junit.Test;
public class ExampleTest {
#Test
public final void test() {
}
}
and build.gradle
apply plugin: 'java'
apply plugin: 'jacoco'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'junit:junit:4.13'
}
jacocoTestReport {
classDirectories.setFrom(
fileTree(dir: "build/classes/java/main")
.filter({file -> !file.name.contains('View') || file.name.contains('ViewModel')})
)
}
Execution of
gradle clean test jacocoTestReport
using Gradle 6.2.1 will generate the following report in directory build/reports/jacoco/test/html/ that doesn't contain View, but contains ViewModel and Example:
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 try to display the tests coverage in SonarQube of my Android Kotlin app of the code in my app module. I can generate the jacoco coverage results and display SonarQube qualymetrics but the problem is that the tests coverage is not displayed in SonarQube :
https://imgur.com/a/xOjxLl1
in my build.gradle of my project I have :
...
dependencies {
classpath 'org.jacoco:org.jacoco.core:0.8.3'
}
...
in my build.gradle of my app module
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'org.sonarqube'
apply plugin: 'jacoco'
jacoco {
toolVersion = '0.8.3'
}
tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}
android {
...
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
testCoverageEnabled true
}
debug {
testCoverageEnabled true
}
testOptions {
animationsDisabled true
unitTests {
returnDefaultValues = true
includeAndroidResources = 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: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/java"
sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
])
}
sonarqube {
properties {
property 'sonar.projectKey', 'mySonarKey'
property 'sonar.projectName', 'myProjectName'
property "sonar.host.url", "http://localhost:9000" // local sonar address
property "sonar.java.binaries", 'target/classes,app/build/tmp/kotlin-classes'
property "sonar.sources", 'src/main/java'
property "sonar.tests", 'src/androidTest/java'
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.coverage.jacoco.xmlReportPaths", "./build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" //link to xml file that generated by jacoco
}
}
...
I generate my jacoco report with :
gradlew clean jacocoTestReport
And generate my sonarqube analysis with :
gradlew sonarqube
the jacoco report is generated myAppName\app\build\reports\jacoco\jacocoTestReport\jacocoTestReport.xml
I also have an html folder next to the report where I can see that my test coverage generated with the right coverage.
So the question is what can I change to display my generated jacoco coverage in SonarQube ?
(I'm not sure about the sonarqube.properties that I add in my app module build.gradle)
I had the same issue for a long time and the solution is to change your path in the build.gradle :
def debugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
To :
def debugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/snapshot", excludes: fileFilter)
Otherwise, the .xml generated won't include the coverage for your whole source code and sonarqube will display 0% coverage as a result
Note : my target folder was called 'snapshot' but it might be different for you
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 currently am using the createDebugAndroidTestCoverageReport to run my android instrumentation tests and generate a coverage report. The only issue that I am running into is that there are packages that are generated from Realm and Databinding and these classes are also being included in my coverage report. How can I configure jacoco to exclude these packages?
test {
jacoco {
append = false
destinationFile = file("$buildDir/jacoco/jacocoTest.exec")
classDumpFile = file("$buildDir/jacoco/classpathdumps")
excludes = []
}
}
Click here for more details on official Jacoco Gradle Plugin
When you are defining the debugTree you can add the fileFilters to be excluded.
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
reports {
xml.enabled = true
html.enabled = true
}
def fileFilter = ['**/package/**']
def debugTree = fileTree(dir: "${buildDir}/intermediates/classes/debug", excludes: fileFilter)
}
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).