I'm using Gradle 1.7 and Jacoco plugin. My project uses Java and Scala plugins.
When I run gradlew -i clean jacocoTestReport
Report is not created and I see in the log
:bl:jacocoTestReport (Thread[Daemon Thread 13,5,main] - start
:bl:jacocoTestReport
Skipping task ':bl:jacocoTestReport' as task onlyIf is false.
:bl:jacocoTestReport SKIPPED
:bl:jacocoTestReport (Thread[Daemon Thread 13,5,main]) - complete
What does it mean? Why report is not created?
The task will only run if coverage data is available. You can make sure of that by also running the test task.
Add the following at a top level to your build.gradle:
test {
finalizedBy jacocoTestReport
}
This means that at the end of the test task the jacocoTestReport task should be run.
You will receive your coverage analysis after run the tests.
None of the above worked for me. What worked for me was the following
Add to the top of my build.gradle:
apply plugin: 'jacoco' // code coverage reports
Add the following as a 'task':
// Generate code coverage reports ... run with jacoco
jacocoTestReport{
additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/reports/jacoco/html"
}
executionData = files('build/jacoco/test.exec')
}
Add the following to your gradle test task:
finalizedBy jacocoTestReport
Then I issued the following command:
gradle run test jacoco
For me the issue was that the executionData.setFrom(executionSource) file path was wrong.
For Spring 2.5 Users, who got stuck with it for hours -just like myself.
I was not having the exec file generated.
And because of that ,
I found that the jacocoTestReport was simply "skipped".
I got it fixed by adding :
apply plugin: 'jacoco'
test {
useJUnitPlatform()
finalizedBy jacocoTestReport // report is always generated after tests run
}
jacocoTestReport {
...
...
...
...
}
That's because I'm using Junit5 with spring boot 2.X
And as of today Junit5 is not by default in the test task,
Hope this helps.
Related
Code Coverage is showing 0% on dashboard
build.gradle file
plugins {
id "org.sonarqube" version "2.8"
id "java"
id "idea"
id "jacoco"
}
jacoco {
toolVersion = "0.8.5"
}
jacocoTestReport {
reports {
html.enabled true
xml.enabled true
xml.destination file("${buildDir}/reports/jacoco.xml")
}
}
plugins.withType(JacocoPlugin) {
tasks["test"].finalizedBy 'jacocoTestReport'
}
sonarqube {
properties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.host.url", "http://169.254.1.100:9000"
property "sonar.coverage.jacoco.xmlReportPath", "${buildDir}/reports/jacoco.xml"
}
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
// https://mvnrepository.com/artifact/junit/junit
testCompile 'junit:junit:4.12'
}
check.dependsOn jacocoTestReport
Running this command
./gradlew build jacocoTestReport sonarqube
The JacocoTestReport gets generated with the correct code coverage
Sonarqube gradle task produces this log
> Task :sonarqube
SonarScanner will require Java 11 to run starting in SonarQube 8.x
Property 'sonar.jacoco.reportPath' is no longer supported. Use JaCoCo's xml report and sonar-jacoco plugin.
Property 'sonar.jacoco.reportPaths' is no longer supported. Use JaCoCo's xml report and sonar-jacoco plugin.
Been Googling for half a day, and the only real solutions to this problem is the following:
Property 'sonar.jacoco.reportPath' is deprecated. Please use 'sonar.jacoco.reportPaths' instead
This answer here explains the double output of:
Property 'sonar.jacoco.reportPaths' is no longer supported. Use JaCoCo's xml report and sonar-jacoco plugin.
However this seems to not have been added to the gradle plugin as the plugin being used is 2.8, the lastest as of posting.
Is there something I'm missing?
You have to enable XML report property as true.
xml.enabled true
To expand on qasanov's answer, I had to add this to my build.gradle file in order for JaCoCo to generate the XML report, which was then picked up automatically by SonarQube:
jacocoTestReport {
reports {
xml.required = true
}
}
The issue in your configuration is type of the property name. It is sonar.coverage.jacoco.xmlReportPaths and not sonar.coverage.jacoco.xmlReportPath
I am not using the gradle sonar plugin, but using Jenkin Job's -> Execute SonarQube Scanner configuration.
By default Jacoco generates only html files, for SonarQube we need xmlReportPath.
Below code in gradle will enable the xml reporting and will generate the file with default name as jacocoTestReport.xml
jacocoTestReport {
reports {
xml.enabled true
}
}
This generates the following file in Jenkins workspace at location /ws/build/reports/jacoco/test/jacocoTestReport.xml along with
/ws/build/reports/jacoco/html folder which contains all the html file for the coverage reports. This report can be accessed by accessing index.html file located at /ws/build/reports/jacoco/html/index.xml
And path to the Jacoco xml report file to be provided in the below property
sonar.coverage.jacoco.xmlReportPaths=<rootFolder>/build/reports/jacoco/test/jacocoTestReport.xml
This did work for me.
Before this in SonarQube I was not able to see the Coverage and in other project Coverage was shown as 0.0%.
So, in summary SonarQube is not able to see your JaCoCo report file.
Java 8 and Gradle 4.6 here. I have a Spring Boot app with the following build.gradle:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.8.RELEASE")
}
}
plugins {
id 'java-library'
id 'checkstyle'
id 'jacoco'
}
apply plugin: 'org.springframework.boot'
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
configurations {
dev
}
dependencies {
compile(
,'ch.qos.logback:logback-classic:1.2.3'
,'org.slf4j:jul-to-slf4j:1.7.25'
,'org.apache.logging.log4j:log4j-to-slf4j:2.9.1'
,'org.apache.commons:commons-lang3:3.7'
,'org.springframework.boot:spring-boot-starter-actuator'
,'org.apache.commons:commons-text:1.2'
,'commons-beanutils:commons-beanutils-core:1.8.3'
)
testCompile(
'junit:junit:4.12'
,'org.mockito:mockito-core:2.23.0'
)
dev('org.springframework.boot:spring-boot-devtools')
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
String buildName = 'myapp'
jar {
baseName = buildName
}
bootRun {
if(project.hasProperty('debugMode')) {
jvmArgs = [ "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" ]
}
classpath = sourceSets.main.runtimeClasspath + configurations.dev
}
checkstyle {
toolVersion = '8.12'
ignoreFailures = false
}
jacocoTestReport {
reports {
xml.enabled false
html.enabled true
html.destination file("${buildDir}/reports/jacoco/")
}
}
check.dependsOn jacocoTestCoverageVerification
jacocoTestCoverageVerification.dependsOn jacocoTestReport
So this is a Spring Boot Java app that also uses the Checkstyle and Jacoco Gradle plugins.
I consider a "full build" to be an invocation that:
Compiles
Runs Checkstyle
Runs unit tests (JUnit)
Runs Jacoco for code coverage analysis
Uses Spring Boot's libraries to build a "fat" (self-contained) executable jar
Given my current Gradle build file, I run a full build like so:
./gradlew clean build
However this can take several minutes to run through all the unit tests and has become cumbersome. I would like to introduce a
"quick build" option that only compiles the code and creates the Spring Boot fat jar for me. This will help speed up development
time tremendously.
I'm hoping to invoke the quick build like so:
./gradlew clean quickbuild
So far I've got this:
tasks.register("quickbuild") {
doLast {
// ???
}
}
But not sure how to link the compilation and fatjar tasks to it (and more importantly; skipping all the other stuff that I don't want!). Any ideas as to what I'm missing?
Update
The bootJar task doesn't seem to exist or be configured (please check my build.gradle file provided above!):
$ ./gradlew clean bootJar
FAILURE: Build failed with an exception.
* What went wrong:
Task 'bootJar' not found in root project 'myapp'. Some candidates are: 'bootRun'.
* Try:
Run gradlew tasks to get a list of available tasks. 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.
* Get more help at https://help.gradle.org
BUILD FAILED in 0s
When I try to run bootRun:
$ ./gradlew clean bootRun
It tries to actually run my app! That's not what I want! I just want to compile and build the fat jar!
See documentation from the Java plugin here : https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks
You could create a new task (quickbuild) and make it depend on the desired task (in your case it could be the assemble lifecycle task, I guess, or maybe bootJar task (for SpringBoot v2.x) or bootRepackage (for SpringBoot v1.5.x) )
tasks.register('quickbuild'){
dependsOn assemble
}
But if the only purpose of quickbuild task is to trigger the creation of the Jar, the simpliest solution is to execute assemble directly
./gradlew clean assemble
I'm currently using a gradle multi module java project with good coverage and sonarqube 6.2 with sonarJava plugin 4.10.0.1026. I'm using Gradle 4.0.1, sonarqube plugin 2.5 and jacoco 0.7.9! The code is java 8.
Because of API driven development the API tests are written as abstract tests in the API projects and called from the implementation projects providing the constructors for the tests.
When analyzing the projekt on the sonarqube server the coverage for the implementation projects is measured correctly but the API projects included in the tests of the IMPL projects are on 0.0% coverage. The coverage results for these projects are ignored.
When simply using the jacoco plugin I was able to get the same behaviour. After doing some research I found a solution to get proper jacoco reports:
task codeCoverageReport(type: JacocoReport) {
description "Creates a unified JaCoCo test report for the project."
// Gather execution data from all subprojects
// (change this if you e.g. want to calculate unit test/integration test coverage separately)
executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")
// Add all relevant sourcesets from the subprojects
subprojects.each {
sourceSets it.sourceSets.main
}
reports {
xml.enabled true
html.enabled true
html.destination file("${buildDir}/reports/jacoco")
csv.enabled false
}
}
// always run the tests before generating the report
codeCoverageReport.dependsOn {
subprojects*.test
}
My current result is the following:
JaCoCo:
JaCoCo (codeCoverageReport-Task)
73% Instruction Coverage
91% Branch Coverage
Sonar
43.1% Line Coverage (only ~30% lines considered in calculation!)
82.1% Condition Coverage (only ~20% conditions covered!)
So the coverage results in sonar are not usable. I have read an post announcing the "sonar.jacoco.reportPaths"-parameter starting with sonar 6.2 and I think java-analyzer 4.4 or sth. like that. When adding this parameter to my gradle build script, the script does not compile anymore. When adding the jacoco .exec files to sonar via sonar project administration nothing changes.
It would be great if there would be a way to manage sonar to calculate the correct coverage.
Thx #Lance Java! He pushed me to a cleaner solution than the one below. If all subprojects have jacoco reports this works aswell. If like me there is only a report in few projects the original solution seems to work better.
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
}
}
}
[...]
task jacocoMerge( type: JacocoMerge ) {
dependsOn( subprojects.jacocoTestReport.dependsOn )
mustRunAfter( subprojects.jacocoTestReport.mustRunAfter )
destinationFile = file( "${buildDir}/jacoco/mergedTests.exec" )
executionData = files( subprojects.jacocoTestReport.executionData )
.filter { jacocoReportFile -> jacocoReportFile.exists() }
}
tasks.sonarqube.dependsOn jacocoMerge
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
Original answer:
It took some time to manage to get the correct coverage data to sonar. There were multiple issues to solve. Sometimes Sonar lost track of the jacoco changes in the classes, so the tests needed the parameter:
append=true
This did not do all the work. There was still an issue in collecting the cross-project coverage. Best solution therefore was to force jacoco to write coverage data to a single .exec file and to hand this to sonar.
Final solution looks like this:
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
destinationFile = file( "${rootProject.buildDir}/jacoco/jacocoTest.exec" )
}
}
}
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
Now sonar has the correct coverage data for my project. After adding some additional tests this is the result:
Total Coverage 91.6%
Line Coverage 91.7%
Condition Coverage 91.3%
Uncovered Lines 36
Uncovered Conditions 11
Lines to Cover 433
Unit Tests 1,114
Unit Test Errors 0
Unit Test Failures 0
Skipped Unit Tests 0
Unit Test Success (%) 100.0%
Unit Test Duration 4s
Hope this may help some of you... ;)
If your tests are in a different project to the sources that you want coverage reports on then you'll need to set additionalSourceDirs and additionalClassDirs. Eg:
evaluationDependsOn ':foo'
task codeCoverageReport(type: JacocoReport) {
additionalSourceDirs.add project(':foo').sourceSets.main.java.sourceDirectories
additionalClassDirs.add project(':foo').sourceSets.main.output.classesDirs
// etc
}
I'm not sure I understand why it's an issue for only some projects to have jacoco and other projects not. You can use Gradle's rich API's (eg TaskCollection and Project) to find them dynamically.
Eg:
[':project1', ':project3', ':project5'].each {
project(it) {
apply plugin: 'java'
apply plugin: 'jacoco'
}
}
project(':merger') {
Collection<Project> jacocoProjects = allprojects.findAll { it.plugins.hasPlugin('jacoco' }
evaluationDependsOn jacocoProjects
task jacocoMerge(type: JacocoMerge) {
dependsOn jacocoProjects*.tasks.withType(Test)
executionData jacocoProjects*.tasks.withType(Test)
}
task mergedReport(type: JacocoReport) {
dependsOn jacocoMerge
executionData jacocoMerge.destinationFile
sourceDirectories.add(files(jacocoProjects*.sourceSets*.java.srcDirs))
classDirectories.add(files(jacocoProjects*.sourceSets*.output.classesDir))
}
}
Here is part of my build.gradle which I have configured to exclude the classes from code coverage.
test {
jacoco {
append = false
destinationFile = file("$buildDir/reports/jacoco/jacoco.exec")
}
finalizedBy jacocoTestReport
}
jacocoTestReport {
reports {
xml {
enabled true
}
html {
enabled true
}
}
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it,
exclude: ['**/*DAO*.*'])
})
}
}
Here I want to exclude DAO classes in my project, as I am mocking those classes anyways, and I am planning to cover them in DB test cases differently from Unit testing.
When I run below command:
gradle jacocoTestReport
It generates the code coverage report which excludes those classes from code coverage,
${project_dir}\build\reports\jacoco\test\html\index.html
As a reason I have excluded those from gradle build via:
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it,
exclude: ['**/*DAO*.*'])
})
}
But when Jenkins builds the project the code coverage reports generated by jenkins doesn't consider this settings, and the report which is generated by jenkins includes those classes, in coverage report.
Another work around I have tried is to modify the code configuration from 'Post-build Actions' for jacoco plugin in jenkins
I have set my pattern under Exclusions (e.g.: **/*DAO*.*) text field, and this is working fine, test cases are getting excluded from code coverage.
But I wanted to know that is there any way I can avoid configuring jenkins for specifing exclusion patterns and uses the reports genrated by gradle jacocoTestReport on jenkins, because this redudant because I am specifing it in build.gradle and If I have to create multiple branches on jenkins then It is kind of maintainance as well.
According to below post this issue is not yet resolved
https://issues.jenkins-ci.org/browse/JENKINS-15570
Is any one has idea on this?
Note: My Jacoco version is :
jacoco {
toolVersion = "0.7.6.201602180812"
}
and Jacoco plugins version on jenkins is :
2.0.1
Project structure:
src/main/java
src/main/resources
src/test/java
Gradle version : 2.2.1
Here is my build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'jacoco'
version = '1.0'
sourceCompatibility = 1.7
targetCompatibility = 1.7
test {
include 'src/test/java'
finalizedBy jacocoTestReport
}
jacoco {
toolVersion = "0.7.6.201602180812"
}
jacocoTestReport {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
additionalSourceDirs = files(sourceSets.main.allJava.srcDirs)
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/reports/jacoco/html"
}
}
when I run gradle task as "test jacocoTestReport", I am getting the below results
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:jacocoTestReport SKIPPED
can someone please suggest what should be added to execute jacoco test report.
Thanks.
The task will only run if coverage data is available. You can make sure of that by also running the test task.
Source - Running jacocoReport
I was able to generate the code coverage results with the following set up.
apply plugin: 'jacoco'
jacocoTestReport {
reports {
xml.enabled false
csv.enabled false
html.destination "${buildDir}/jacocoHtml"
}
}
You can force it to run with:
jacocoTestReport {
onlyIf = {
true
}
}
This will probably give an error (there's a reason it didnt run in the first place), but the error should give more information.
Unfortunately, none of these answers worked for me.
For Spring 2.5 Users, who got stuck with it for hours -just like myself.
I had a similar issue.
I was not having the exec file generated.
And because of that ,
I found that the jacocoTestReport was simply "skipped".
I got it fixed by adding :
test {
useJUnitPlatform()
finalizedBy jacocoTestReport // report is always generated after tests run
}
That's because I'm using Junit5 with spring boot 2.X - Gradle 7.1
And as of today Junit5 is not invoked by default in the test task.
Late at party, but none of the answers above solved for me. Instead, changing
dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-api:5.0.0-M2'
}
to
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter:5.5.2'
}
worked like a charm (source).