[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 uses 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, when you scan a project with Sonar scanner, we use a special Jacoco report analyzer, which imports your 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 that 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 that 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 the 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 the 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, the 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'
}
27 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
Java coverage @ 0.0% with JacocoXMLReport
Unable to push jacoco.exec to SonarQube
Sonar Gradle Coverage Stopped Publishing
Sonarqube 8.1, Code Coverage branches 0%
In SonarQube 8.2, code coverage is always showing 0%
Jacoco report don't show on SonarQube
Aggregated Coverage for Multi-Module Maven Project
Mule Project - No report imported, no coverage information will be imported by JaCoCo XML Report Importer
Trouble generating JaCoCo XML format
How to configure/upload coverage reports to SonarCloud.io?
Java analyzer v6.0: more accurate analysis, with fewer false-positives
CodeCoverage stays at 0.00%
Code coverage is 0 for gradle project- which built via cloudbess jenkins
How to generate Jacoco Report and upload to Sonar qube
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
JaCoCo import troubleshooting
JaCoCo coverage is different in Sonar
How to explain differences between Jacoco report and Sonarqube dashboard?
Code Coverae is 0%, unit tests in place
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 vary from GCP slave to AWS slave
JaCoCo support for SonarQube 8.6
GitHub Actions setup for maven and ts
Take merged jacoco report into consideration for multimodule project
Suggestion of update for documentation
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
Not getting code coverage in SonarCloud from Bitbucket (Java17)
Sonarqube coverage does not match the Jacoco report it has imported
Not scanning kotlin source
Code coverage is not showing in SonarQube 8.9
PR showing 0% code coverage, yet jacoco.xml file exists
Code Coverage is 0% in Sonarqube for Mockito tests
How to get Code Coverage showing in Sonar Cloud for maven project
Passing Java 11 to SonarQube using Gradle with Java 8
SonarQube Code Coverage always shows 0%
Sonarqube coverage for multimodule projects with integration tests
SonarQube 9.1- Coverage report is 0%
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
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
Take merged jacoco report into consideration for multimodule project
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
How to get sonar-jacoco-plugin
Jacoco 0 code coverage since switching to xmlReportPaths
SonarCloud recognizing tests, but not their coverage

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

3 Likes