Gradle, Kotlin, Junit5 and Jacoco - 0% coverage reported - gradle

We would like to use Gradle in conjunction with Kotlin and Jacoco (+JUnit 5) for generating a code coverage report.
Our project directory tree looks as follows:
project/{src,test}/main/kotlin ...
Our build.gradle file looks as follows:
jacoco {
toolVersion = "0.7.9"
reportsDir = file("$buildDir/reports")
applyTo junitPlatformTest
}
jacocoTestReport {
group = "Reporting"
description = "Generate Jacoco coverage report."
classDirectories = fileTree(
dir: "$buildDir/classes/kotlin/main"
)
def coverageSourceDirs = [
"src/main/kotlin"
]
additionalSourceDirs = files(coverageSourceDirs)
sourceDirectories = files(coverageSourceDirs)
executionData = files("$buildDir/jacoco/junitPlatformTest.exec")
reports {
xml.enabled = true
html.enabled = true
csv.enabled = true
}
}
test {
jacoco {
append = false
destinationFile = file("$buildDir/jacoco/junitPlatformTest.exec")
includeNoLocationClasses = true
}
}
test.dependsOn junitPlatformTest
With this configuration, Jacoco generates a html report and puts it under build/reports/test/html. However, it shows me 0% coverage. This should not be the case, because I have one test case that excercices all methods for a single dummy class in the project.
I went through several posts such as:
http://vgaidarji.me/blog/2017/12/20/how-to-configure-jacoco-for-kotlin-and-java-project/
Jacoco is reporting 0 coverage of Kotlin classes by unit tests, in an Android project
JaCoCo returning 0% Coverage with Kotlin and Android 3.0
However, I could not quite find a solution that worked for me, yet.

I recently hit this issue when upgrading form junit 4 -> 5. In my case I had to re-arrange the declaration of the dependencies as outlined here. If you change to this format you should see the coverage reported correctly again without having to change any other configuration.
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}

Related

jacocoRootReport only shows coverage from last project of multi-project gradle build

I'm upgrading my Gradle 4 multi-project setup to Gradle 6. I've followed instructions here:
https://stackoverflow.com/a/56181389
and I've bumped the jacoco version to 0.8.5. Right now the only problem is that the human-readable coverage report seems to be missing most of the coverage data that it was showing under the old version. It seems that the coverage report is only reflecting the last (most recently tested) project. It used to work fine under Gradle 4. I'm using Java 8.
I ran the gradle build using --debug, and I notice that the test.exec file is deleted repeatedly, once for each subproject that has tests. I think this is the problem, but I don't know how to prevent deletion of this file.
2020-04-16T09:16:21.048-0600 [DEBUG] [org.gradle.internal.file.impl.DefaultDeleter] Deleting /Users/bishop/dev/aep/edge-profile-lookup-target/build/jacoco/test.exec
Can someone please help me fix this so that all of the coverage (from each of the tests which ran against each of the sub projects) appear in a single coverage report?
Here are the parts of the main build.gradle file that seem relevant:
buildscript {
ext {
jacocoVersion = '0.8.5'
...
}
...
}
allprojects {
...
apply plugin: 'jacoco'
...
}
subprojects {
tasks.withType(Test) {
// redirect all coverage data to one file
jacoco {
destinationFile = file("$rootProject.buildDir/jacoco/test.exec")
}
}
jacoco {
toolVersion = jacocoVersion
}
jacocoTestReport {
additionalSourceDirs.from = files(sourceSets.main.allSource.srcDirs)
sourceDirectories.from = files(sourceSets.main.allSource.srcDirs)
classDirectories.from = files(sourceSets.main.output.collect {
fileTree(dir: it, exclude: project.properties['BUILD_COVERAGE_EXCLUSIONS'].tokenize(','))
})
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
}
}
task jacocoRootReport(type: JacocoReport) {
dependsOn = subprojects.test
additionalSourceDirs.from = files(subprojects.sourceSets.main.allSource.srcDirs)
sourceDirectories.from = files(subprojects.sourceSets.main.allSource.srcDirs)
classDirectories.from = files(subprojects.jacocoTestReport.classDirectories)
executionData.from = files(subprojects.jacocoTestReport.executionData)
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
onlyIf = {
true
}
doFirst {
executionData.from = files(executionData.findAll {
it.exists()
})
}
}
...
apply plugin: 'jacoco'
configurations.create("jacoco")
configurations {
jacoco
}
dependencies {
jacocoAnt group: 'org.jacoco', name: 'org.jacoco.ant', version: jacocoVersion
jacocoAnt group: 'org.ow2.asm', name: 'asm', version: asmVersion
}
task copyRuntimeLibs(type: Copy) {
from configurations.jacoco
into "$rootProject.buildDir/libs"
}
build.dependsOn copyRuntimeLibs
The following:
jacoco {
destinationFile = file("$rootProject.buildDir/jacoco/test.exec")
}
configures Jacoco to always use the same output file.
So the issue is most likely that more recent Gradle versions work on separating colliding outputs.
I would recommend looking at the recently published sample on how to setup Jacoco in a multi project instead of attempting to rely on colliding outputs.

How to get Jacoco reports for the Karate test feature files using gradle

How to get Jacoco reports for the Karate test feature files using Gradle.
My project is a Gradle project and I am trying to integrate jacoco report feature in my project for the karate tests. The server is running in my local on 8080 port.
I am doing the following way to generate jacoco report and please let me know is my approach correct and also give me a solution to get the jacoco report for the gradle project.
1) First I am trying to generate jacoco execution data with the help of jacocoagent.jar as follows with a Gradle task:
java -javaagent:/pathtojacocojar/jacocoagent.jar=destfile=/pathtojocofile/jacoco.exec -jar my-app.jar
2) Next, I am running a Gradle task to generate the report
project.task ('jacocoAPIReport',type: org.gradle.testing.jacoco.tasks.JacocoReport) {
additionalSourceDirs = files(project.sourceSets.main.allSource.srcDirs)
sourceDirectories = files(project.sourceSets.main.allSource.srcDirs)
classDirectories = files(project.sourceSets.main.output)
executionData = fileTree(dir: project.projectDir, includes: ["**/*.exec", "**/*.ec"])
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
onlyIf = {
true
}
doFirst {
executionData = files(executionData.findAll {
it.exists()
})
}
}
project.task('apiTest', type: Test) {
description = 'Runs the api tests'
group = 'verification'
testClassesDirs = project.sourceSets.apiTest.output.classesDirs
classpath =
project.sourceSets.apiTest.runtimeClasspath
useJUnitPlatform()
outputs.upToDateWhen { false }
finalizedBy jacocoAPIReport
}
I don't see any of my application's classes in the jococo.exec file. I think, bcz of that I am always getting the coverage report as 0%.
The server is running in my local on 8080 port.
I don't think that is going to work. Depending on how your code is structured you need to instrument the code of the server.
I suggest trying to get a simple unit test of a Java method to work with Gradle. If that works, then use the same approach for the server-side code and it will work.

SonarQube scanner cannot scan Kotlin Jacoco coverage

My gradle task jacocoTestReport() works properly and it generates jacocoTestReport.xml. It contains kotlin coverage as expected.
However, if I use SonarQube Scanner 4.0 Jenkins plugin, it fails to send Kotlin part of coverage report. So I cannot find kotlin coverage on Sonarqube dashboard.
I think there is something wrong with my Analysis properties, but I cannot find it.
# required metadata
sonar.projectKey=aaaaa123
sonar.projectName=aaaaa123
sonar.projectVersion=${VERSION_MAJOR}.${VERSION_MINOR}
# disable sonar.language parameter to triggering multi-language analysis
#sonar.language=java
# path to test source directories
sonar.sources=mypicker/src/main/java
sonar.test.inclusions=mypicker/src/test/java
sonar.java.binaries=mypicker/build/intermediates/javac/debug,ext/mypicker/build/tmp/kotlin-classes/debug
#sonar.junit.reportPaths=mypicker/build/test-results/testDebugUnitTest
#sonar.jacoco.reportPaths=mypicker/build/jacoco/testDebugUnitTest.exec
sonar.coverage.jacoco.xmlReportPaths=mypicker/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
sonar.sourceEncoding=UTF-8
sonar.java.source=1.8
sonar.ws.timeout=300
sonar.findbugs.timeout=3600000
My gradle task code which works perfectly is:
task jacocoTestReport(type: JacocoReport, dependsOn: 'testDebugUnitTest') {
reports {
xml.enabled = true
html.enabled = true
}
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
def javaDebugTree = fileTree(dir: "${buildDir}/intermediates/javac/debug", excludes: fileFilter)
def kotlinDebugTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: fileFilter)
def mainSrc = "${project.projectDir}/src/main/java"
sourceDirectories.from = files([mainSrc])
classDirectories.from = files([javaDebugTree, kotlinDebugTree])
executionData.from = files("${buildDir}/jacoco/testDebugUnitTest.exec")
}
Sonarqube: 6.7.5 (build 38563)
SonarQube Scanner: 4.0.0.1744

CorDapp JaCoCo Code Coverage

I have a Corda based project with several CorDapp sub projects. I've been looking to add JaCoCo code coverage to this project. I'm looking to have a single code coverage report draw in an aggregate report of all the subproject JaCoCo reports.
To add JaCoCo to a maven project with several maven sub projects, I followed this blog entry https://lkrnac.net/blog/2016/10/aggregate-test-coverage-report/. After we ran the build ./gradlew clean test and got our reports, one of our team members noted that the whitelists weren't being created properly anymore when we ran ./gradlew clean deployNodes.
I've gone back to the base Kotlin CorDapp template found here https://github.com/corda/cordapp-template-kotlin to rule out if it's something we've done wrong with our project structure/gradle. Without JaCoCo added, I see all the whitelist entries I would expect. Once I add the JaCoCo code, I only see the 5 default Corda whitelist entries, and none of my added contract entries.
I'm using JaCoCo version 0.8.1 and coveralls version 2.6.3. The changes I've made are all within the build.gradle file for the root directory cordapp-template-kotlin:
subprojects {
repositories {
mavenCentral()
}
apply plugin: 'jacoco'
apply plugin: 'java'
group = 'net.lkrnac.blog'
version = '1.0-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
dependencies {
testCompile("junit:junit:4.12")
}
jacoco {
toolVersion = jacoco_version
}
//command for generating subproject coverage reports
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
}
def publishedProjects = subprojects.findAll()
task jacocoRootReport(type: JacocoReport, group: 'Coverage reports') {
description = 'Generates an aggregate report from all subprojects'
dependsOn(publishedProjects.test)
additionalSourceDirs = files(publishedProjects.sourceSets.main.allSource.srcDirs)
sourceDirectories = files(publishedProjects.sourceSets.main.allSource.srcDirs)
classDirectories = files(publishedProjects.sourceSets.main.output)
executionData = files(publishedProjects.jacocoTestReport.executionData)
doFirst {
executionData = files(executionData.findAll { it.exists() })
}
reports {
html.enabled = true // human readable
xml.enabled = true // required by coveralls
}
}
coveralls {
sourceDirs = publishedProjects.sourceSets.main.allSource.srcDirs.flatten()
jacocoReportPath = "${buildDir}/reports/jacoco/jacocoRootReport/jacocoRootReport.xml"
}
tasks.coveralls {
dependsOn jacocoRootReport
}
I believe that the problem is coming from simply adding a task where JacocoReport as a parameter. Any thoughts how I could proceed to have both code coverage, along with building my whitelists correctly?
I have managed to find how to fix the coverage/whitelisting issue. I started stripping away what seemed to be unnecessary code within the subprojects spec, and found that removing everything except the apply plugin:, jacoco, and jacocoTestReport commands yielded both the root Jacoco code coverage, along with the necessary whitelisting. I didn't need to change any of the other code above to get the whitelisting to work.
For reference, subprojects now looks like this:
subprojects {
apply plugin: 'jacoco'
apply plugin: 'kotlin'
jacoco {
toolVersion = jacoco_version
}
//command for generating subproject coverage reports
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination file("${buildDir}/jacocoHtml")
}
}
}

Gradle Jacoco - coverage reports includes classes excluded in configuration

I added to a project a set of sources that don't have tests and I don't want them spoil my test coverage statistics. I configured Jacoco in the next way :
test {
jacoco{
excludes = ['org/bla/**']
includes = ['com/bla/**']
append = false
}
}
jacocoTestReport {
dependsOn test
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled true
html.enabled true
}
classDirectories = fileTree(dir: 'build/classes/main', include: 'com/bla/**')
sourceDirectories = fileTree(dir: 'scr/main/java', include: 'com/bla/**')
}
But anyway, when generating the report, Jacoco also includes classes from org.bla
Can someone help me ?
EDIT
After some debugging, it appears that all default outputs are added to org.gradle.testing.jacoco.tasks.JacocoReport#classDirectories in a private method org.gradle.testing.jacoco.plugins.JacocoPlugin#addDefaultReportTasks
That's visible when using such code :
jacocoTestReport {
classDirectories = files('build/classes/main/com/bla')
println("-----------------------------------------------------------")
getAllClassDirs().each { File file ->
println(file.absolutePath)
}
println("-----------------------------------------------------------")
getAdditionalClassDirs().each{ File file ->
println(file.absolutePath)
}
}
jacocoTestReport << {
println("-----------------------------------------------------------")
getAllClassDirs().each { File file ->
println(file.absolutePath)
}
println("-----------------------------------------------------------")
getAdditionalClassDirs().each{ File file ->
println(file.absolutePath)
}
}
Output
-----------------------------------------------------------
<path_here>\build\classes\main\com\bla
-----------------------------------------------------------
....more text here
-----------------------------------------------------------
<path_here>\build\classes\main\com\bla
<path_here>\build\classes\main
<path_here>\build\resources\main
-----------------------------------------------------------
So - the question is : is it possible to override somehow org.gradle.testing.jacoco.plugins.JacocoPlugin#addDefaultReportTasks method, or override completely org.gradle.testing.jacoco.plugins.JacocoPlugin class ?
Ok, found a workaround :
jacocoTestReport.doFirst{
classDirectories = files('build/classes/main/com/bla')
}
This overrides classDirectories set by JacocoPlugin class.
If you have a task names something other than
test
that runs unit tests, you should be able to generate the report adding the following task to your build.gradle file:
jacocoTestReport {
group = "Reporting"
description = "Generates Jacoco coverage reports for unit tests"
executionData = files('build/jacoco/unitTest.exec')
reports {
html.destination = 'build/reports/coverage'
}
}
This needs to be called explicitly after the task to run unit tests, e.g:
gradle unitTest jacocoTestReport

Resources