Trying to get Code coverage on my Robolectric tests in Android utilising Jacoco but it simply refuses to acknowledge my Robolectric tests when creating the reports.
My jacoco.gradle file is as follows:
apply plugin: 'jacoco'
jacoco {
toolVersion = "0.7.6.201602180812"
}
project.afterEvaluate {
android.applicationVariants.all { variant ->
def name = variant.name
def testTaskName = "test${name.capitalize()}UnitTest"
tasks.create(name: "${testTaskName}Coverage", type: JacocoReport, dependsOn: "$testTaskName") {
group = "Reporting"
description = "Generate Jacoco coverage reports for the ${name.capitalize()} build."
classDirectories = fileTree(
dir: "${project.buildDir}/intermediates/classes/${name}",
excludes: ['**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/*$ViewBinder*.*',
'**/BuildConfig.*',
'**/Manifest*.*']
)
sourceDirectories = files(['src/main/java'].plus(android.sourceSets[name].java.srcDirs))
executionData = files("${project.buildDir}/jacoco/${testTaskName}.exec")
reports {
xml.enabled = true
html.enabled = true
}
}
}
}
With this setup I can get Coverage reports but I get 0% coverage despite having Robolectric tests in "src/test/java".
If I add in the following code to that file:
android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
}
}
}
}
I get the following error when Gradle tries to sync:
Error:No such property: includeNoLocationClasses for class:
org.gradle.testing.jacoco.plugins.JacocoTaskExtension_Decorated
I know that I need to have Gradle 2.13 to use this includeNoLocationClasses value so in graddle-wrapper.properties I have the following:
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions-snapshots/gradle-2.13-20160228000026+0000-all.zip
I am pretty certain I am running Gradle 2.13 with Android plugin version 1.5
In my apps Gradle file I have the following:
//...
apply from: 'jacoco.gradle'
//..
testOptions {
unitTests.returnDefaultValues = true
}
//...
debug {
testCoverageEnabled true
}
And the command I use to run the coverage is:
./gradlew createDebugCoverageReport
So I am wondering why I get the includeNoLocationClasses error despite using the correct Gradle version? And outside of that maybe I am doing something wrong where Jacoco isn't picking up the Robolectric tests in src/test.java ?
I don't see you build.gradle completely, but to have that flag in you have to:
Use gradle 2.13+
Use jacoco 0.7.6.201602180812
You're sure that you use gradle proper version. So, I think, the issue is only in using wrong jacoco.
Mentioning jacoco {toolVersion = "0.7.6.201602180812"} doesn't influence gradle DSL. You should add newer jacoco plugin:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.jacoco:org.jacoco.core:...'
}
}
And you should apply plugin, which you're already doing:
apply from: 'jacoco'
After such configuraiton you don't need jacoco {toolVersion = "..."} more.
Note: consider to update to newer android gradle plugin, 2.2.x is already stable. jacoco also has newer version already 0.7.7.201606060606
One more note: if you see original issue in Android Studio, check that you use wrapper by default and check that you pointed wrapper to gradle 2.13
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.
Overview:
I am trying to upgrade Java and Gradle version in my project as follows:
java from 8 to 11
Gradle from 5 to 6
My project runs with no issues on the old versions but when runs on Java 11 and Gradle 6 it complains about fireBugs plugin and raises the following exception:
> Plugin with id 'findbugs' not found.
Snippet of gradle file:
buildscript {
ext {
SampleProjectVersion = '1.3.4'
}
repositories {
mavenLocal()
maven {
url1
}
}
dependencies {
classpath(sample lib 1)
classpath(sample lib 2)
}
apply plugin: 'findbugs'
...
I would appreciate if you could share your thoughts with me as I couldn't find any proper solution for that.
From the Gradle web site (Upgrading your build from Gradle 5.x to 6.0):
The FindBugs plugin has been removed
The deprecated FindBugs plugin has been removed. As an alternative,
you can use the SpotBugs plugin from the Gradle Plugin Portal.
https://docs.gradle.org/current/userguide/upgrading_version_5.html
You can use the SpotBugs plugin. Try my snippet of gradle file
buildscript {
ext {
SampleProjectVersion = '1.3.4'
}
repositories {
mavenLocal()
maven {
url1
}
}
dependencies {
classpath(sample lib 1)
classpath(sample lib 2)
classpath "gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:2.0.0"
}
apply plugin: "com.github.spotbugs"
tasks.withType(com.github.spotbugs.SpotBugsTask) {
spotbugsMain.enabled = true
spotbugsTest.enabled = true
reports {
xml.enabled = false
html.enabled = true
}
}
...
I am using sonarqube + jacoco for codecoverage, i have junit test case for my java code in below dir structure.
src/main/java/code/abc.java
src/main/test/code/Testabc.java
I want a code coverage on sonarqube dashboard and i am using gradle for the same.
apply plugin: "java"
apply plugin: "org.sonarqube"
apply plugin: "jacoco"
jacoco {
toolVersion = "0.7.9"
reportsDir = file("${project.buildDir}/jacoco/customJacocoReportDir")
}
sonarqube {
properties {
property "sonar.sources", "."
property "sonar.projectName", "javacode"
property "sonar.projectKey", "javacode"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.java.binaries", "/data/tmp/env/"
property "sonar.jacoco.reportPath", "${project.buildDir}/jacoco/test.exec"
}
}
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7"
}
}
I am running gradle sonarqube
I dont know where i am doing wrong? if any one can help to get the coverage of test case written from java code.
It seems to me, that Jacoco test reports are generated in one directory, but SoanrQube is configured to look into another one. Here is the directory you save reports:
reportsDir = file("${project.buildDir}/jacoco/customJacocoReportDir")
and here is Sonar configuration to look up Jacoco results:
property "sonar.jacoco.reportPath", "${project.buildDir}/jacoco/test.exec"
There is customJacocoReportDir directory missed
Here's the minimal config that is working with
gradle 7.5
sonarqube gradle plugin 3.3
jacoco latest version
Gradle snippet:
apply plugin: "org.sonarqube"
apply plugin: "jacoco"
// jacoco task config not needed since I'm using the defaults (latest version, default dir)
// jacoco {
// toolVersion = "0.7.9"
// reportsDir = file("${project.buildDir}/jacoco/customJacocoReportDir")
// }
sonarqube {
properties {
property "sonar.sources", "."
property "sonar.projectName", "javacode"
property "sonar.projectKey", "javacode"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.java.binaries", "/data/tmp/env/"
// no need to define sonar.jacoco.reportPaths or sonar.coverage.jacoco.xmlReportPaths as sonarqube
// finds the xml report in the default location (build/reports/jacoco/test/jacocoTestReport.xml)
// property "sonar.jacoco.reportPath", "${project.buildDir}/jacoco/test.exec"
}
}
jacocoTestReport {
reports {
xml.enabled true // to be sure xml report is created (sonarqube knows jacoco's default report location)
}
}
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