[Coverage & Test Data] Importing JaCoCo coverage report in XML format

Importing JaCoCo coverage report in XML format

Version 5.12 of our SonarJava analyzer deprecated use JaCoCo’s binary format (.exec files) to import coverage. This binary format is internal to the JaCoCo project, and as such there are no guarantees for backward compatibility, so it should not be used for integration purposes.

As a replacement, we developed the sonar-jacoco plugin, which imports JaCoCo’s XML coverage report, and this is the preferred option now. In this guide, I will describe how to import this XML report in some common scenarios.

You can find sample projects using the setup described here in this repository.

Maven

Remove any use of the sonar.jacoco.itReportPath, sonar.jacoco.reportPath, and sonar.jacoco.reportMissing.force.zero properties; they are deprecated and related functionality will be removed in the future.

We will use the jacoco-maven-plugin and its report goal to create a code coverage report. Usually, you would want to create a specific profile which executes unit tests with the JaCoCo agent and creates a coverage report. This profile would then only be activated if coverage information is requested (e.g., in the CI pipeline).

In the most basic case we will need to execute two goals: jacoco:prepare-agent which creates the command line argument for JVM running the tests, and jacoco:report which uses data collected during unit test execution to generate a report in html, xml or csv format.

Here is an example of such a profile

<profile>
  <id>coverage</id>
  <build>
   <plugins>
    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
      <execution>
        <id>prepare-agent</id>
        <goals>
         <goal>prepare-agent</goal>
        </goals>
      </execution>
      <execution>
        <id>report</id>
        <goals>
         <goal>report</goal>
        </goals>
      </execution>
    </executions>
    </plugin>
   </plugins>
  </build>
</profile>

By default the generated report will be saved under target/site/jacoco/jacoco.xml; this location will be checked automatically by the sonar-jacoco plugin so no further configuration is required. Just launch mvn sonar:sonar as usual and the report will be picked up.

If you need to change the directory where the report has been generated you can set the property either on the command line using maven’s -D switch

mvn -Dsonar.coverage.jacoco.xmlReportPaths=report1.xml,report2.xml sonar:sonar 

or you can set the property inside your pom.xml

    <properties>
        <sonar.coverage.jacoco.xmlReportPaths>../app-it/target/site/jacoco-aggregate/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
    </properties>

Multi-module builds

With multi-module builds, we sometimes need to show coverage across modules. For example, we might have multiple modules implementing business logic and another module which contains integration tests for all these modules. We would like to see also coverage from this integration test module on business logic modules.

To achieve this, we can use JaCoCo’s report-aggregate goal, which will collect coverage information from all modules and create a single report with coverage for the whole project.

First, we still have to use prepare-agent goal as we did in basic example in all modules, the best way to achieve this is to configure the execution of this goal in the parent pom.xml. Then we need to add execution to the module containing integration tests to generate the report across modules

<build>
  <plugins>
    <plugin>
      <groupId>org.jacoco</groupId>
      <artifactId>jacoco-maven-plugin</artifactId>
      <executions>
        <execution>
          <id>report</id>
          <goals>
            <goal>report-aggregate</goal>
          </goals>
          <phase>verify</phase>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

We snippet binds the goal to the verify phase. Now when we execute

mvn clean verify

we should find the aggregated report in target/site/jacoco-aggregate/jacoco.xml of the module containing the integration tests.

However, the JaCoCo plugin imports coverage reports module by module, so we need to import the same report multiple times (once for every module) to have coverage for all modules. To achieve this we set the property sonar.coverage.jacoco.xmlReportPaths in every module

<properties>
  <sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/../path_to_module_with_report/target/site/jacoco-aggregate/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties>

Troubleshooting

To investigate issues with the import of coverage information you can run Maven with the debug flag, -X:

mvn -X clean verify sonar:sonar 

In the logs, you will find the execution of different sensors for each module of the project. Typically you will have a log similar to the following one when XML report is processed.

[INFO] 16:58:05.074 Sensor JaCoCo XML Report Importer [jacoco]
[DEBUG] 16:58:05.082 Reading report 'C:\projects\sonar-scanning-examples\maven-multimodule\tests\target\site\jacoco-aggregate\jacoco.xml'
[INFO] 16:58:05.093 Sensor JaCoCo XML Report Importer [jacoco] (done) | time=19ms

Gradle

Gradle includes the JaCoCo plugin in its default distribution. To apply the plugin to a project, you need to declare it in your build.gradle file together with the SonarScanner for Gradle.

plugins {
    id "jacoco"
    id "org.sonarqube" version "2.7.1"
}

The plugin provides the jacocoTestReport task, which needs to be configured to produce an XML report.

jacocoTestReport {
    reports {
        xml.enabled true
    }
}

By default report will be saved under the build/reports/jacoco directory and this location will be picked up automatically by the sonarqube plugin, so there is no further configuration required. To import coverage, launch

gradle build jacocoTestReport sonarqube

It is convenient to execute the jacocoTestReport task every time we execute test with the JaCoCo agent; to achieve this, we can run it after the test task. We can use finalizedBy to create a dependency between test and jacocoTestReport.

plugins.withType(JacocoPlugin) {
  tasks["test"].finalizedBy 'jacocoTestReport'
}
24 Likes
sonar.coverage.jacoco.xmlReportPaths not showing code coverage but the deprecated sonar.jacoco.reportPaths shows code coverage
Coverage data show 0% not consistent with JaCoCo exec file showing ~ 45%
Jacoco Coverage: switch from deprecated Binary to XML format in a Tycho build shows 0%
Best way to report Jacoco data to sonarqube from jenkins(Unit test) and remote dockerhost(Integration tests)
Jacoco XML format cannot work as good as the binary format for multi-module maven
Multi-module Maven Project with Jacoco
How to get integration test code coverage using jacoco
Code coverage 0% by unit tests
Sonarqube 8.1, Code Coverage branches 0%
Unable to push jacoco.exec to SonarQube
Sonar Gradle Coverage Stopped Publishing
Jacoco report don't show on SonarQube
How to configure/upload coverage reports to SonarCloud.io?
Java analyzer v6.0: more accurate analysis, with fewer false-positives
Aggregated Coverage for Multi-Module Maven Project
CodeCoverage stays at 0.00%
How to generate Jacoco Report and upload to Sonar qube
Trouble generating JaCoCo XML format
Java coverage @ 0.0% with JacocoXMLReport
In SonarQube 8.2, code coverage is always showing 0%
How to get sonar-jacoco-plugin
Jacoco 0 code coverage since switching to xmlReportPaths
SonarCloud recognizing tests, but not their coverage
JaCoCo import troubleshooting
JaCoCo coverage is different in Sonar
How to explain differences between Jacoco report and Sonarqube dashboard?
Jacoco code coverage from Maven doesn't work anymore (azure devops)
Code coverage shows as 0%
Coverage is 0 in SonarQube 7.9.4
Code coverage not reflecting in SonarQube UI
JaCoCo Plugin Documentation
Sonarcloud.io ignores jacoco xml report
How to SCAN and check code coverage of multiple modules in same project
Java Project with multi modules
Sonarqube not generating CodeCoverage report for both Jacoco or Cobertura
Proyecto de maven que no refleja la covertura en sonaqube
No coverage report can be found with sonar.coverage.jacoco.xmlReportPaths on Jenkins
Ddnot find jacocoant.jar plugin
Sonarqube not generating CodeCoverage report for both Jacoco or Cobertura
Code Coverage not showing in Sonar Cloud though coming in Code Coverage Tab under Azure Devops Pipeline
Code coverage is 0 for gradle project- which built via cloudbess jenkins
Code coverage vary from GCP slave to AWS slave
JaCoCo support for SonarQube 8.6
GitHub Actions setup for maven and ts
SonarQube 9.1- Coverage report is 0%
SonarQubeProperties in gradle plugin 3.3 help
Enabling report aggregate in all modules in maven multimodule project
How to explain differences between Jacoco report and Sonarqube dashboard?
SonarQube and code coverage
Upgrading from Sonar 8.9.2 to Sonar 9.4, problem with project coverage with jdk 11
Azure Devops pipeline not publishing code coverage results in Sonarcloud for Java project
Sonar coverge is not generating for the code repo
Jacoco coverage report in XML format using ant
Suggestion of update for documentation
Sonarqube coverage does not match the Jacoco report it has imported
Not scanning kotlin source
Not getting code coverage in SonarCloud from Bitbucket (Java17)
Jacoco report code coverage
How to configure jacoco codecoverage to a multi module maven project
Sonarcloud says I have 0% Code coverage even though I wrote unit tests
Code Coverage not in Pull Request decoration
Difference in %age coverages
JavaCode coverage with ECLEmma
Upload jacocoTestReport data to sonarcloud using github actions
Does generation of Java Code Coverage report require attaching Jacoco agent to application?
Trouble generating JaCoCo XML format
Code coverage is showing as 0.0% while i can see the code bugs, vulnerabillity and code smell issues
Jacoco.exec or java .class files are not detected by sonar scanner - throwing indexed with language 'null'
My jacoco report were being uploaded to sonarcloud
SonarJava 6.1 upgrade results in a massive performance drop, coverage data gone
Code coverage of multimodule project is incomplete
Compatibility of LTS/non LTS
Mule Project - No report imported, no coverage information will be imported by JaCoCo XML Report Importer
Maven sample does not work with JUnit 5 - Zero coverage
Analyze Java 13 via Gradle
I am unable to import coverage.xml from Azure DevOps
Jacoco report don't show on SonarQube
Issue while generating SonarQube (8.9) report in multi-maven project
No Coverage in Multi-module Maven Build with Jacoco
SonarQube community version 8.3 is showing 0% code coverage, however with the same configurations I am able to get coverage in community version 8.1
Error in Travis Log: "You're not authorized to run analysis. Please contact the project administrator" and Travis CI org not sending Jacoco reports to SonarCloud
Noticed code coverage difference from Version 6.4 (build 25310) to Version 7.9.3 (build 33349)
CodeCoverage stays at 0.00%
How can I import Jacoco test report using sonarscaner
How can I import Jacoco test report using sonarscaner
Coverage on new code is always – on many Java projects
sonar.coverage.jacoco.xmlReportPaths is not defined after upgrading SonarQube

Gradle Multi-Module solution? Should we downgrade to SonarQube 7.9?

You cite a Maven multi-module solution to aggregate data across projects. With the old jacoco exec format, there is a Gradle multi-module solution with the jacocoMergeTest from JacocoMerge (part of gradle jacoco plugin). The Gradle JacocoMerge documentation doesn’t clearly list an XML data aggregation or merge.

The Version 8.* SonarQube no longer supports the exec data that jacocoMargeTest was aggregating for us. Does this mean we need to downgrade back to 7.9? We’ve lost Coverage tracking since we upgraded and I’m finding no solutions in my searches.

4 Likes

An update. It is looking like the gradle plugin is able to consolidate all the different module xml reports into the coverage data. Unfortunately, the coverage statistics went down by nearly half. So something is amiss. But at least it’s partially working. More investigation is needed here, and any insights from SonarSource would still be greatly appreciated.

2 Likes

Do you please know where could I find some actual and functioning guide on how to make gradle jacoco and sonarqube multi project build work with sonarqube 7.9 ? Thank you very much.

@Patrik_Polacek - if you are still using SonarQube 7.9, you should try the JacocoMerge solution described here: https://github.com/gradle/gradle/issues/8881#issuecomment-477600280

That is, if you are using Gradle. Not sure how with Maven. Good luck.

1 Like

For multi-module Maven builds, generate the comma-seperated list of xml files (one per module) dynamically by using the command

find . -path '*jacoco.xml' | sed 's/.*/${maven.multiModuleProjectDirectory}\/&/' | tr '\n' ','

and copy it to the sonar.coverage.jacoco.xmlReportPaths property. Alternatively pass the property in using the command line:

mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install org.jacoco:jacoco-maven-plugin:report

mvn sonar:sonar -Dsonar.coverage.jacoco.xmlReportPaths=$(find "$(pwd)" -path '*jacoco.xml' | sed 's/.*/&/' | tr '\n' ',')
7 Likes

Hi,

We believe it’s better if we don’t add messages in the “Guides” post. So if you have some question or addition, please create a new thread and if it will make sense our team will update the guide.

Thanks for the understanding and I’m locking the thread

2 Likes