Tag Archives: sonarcloud

How to get test coverage back in sonarcloud maven builds

I use travis-ci to build my github projects and use sonarcloud to do analysis of the builds.

In the start of January 2020, the test coverage percentage on all sonarcloud projects suddenly dropped to 0%.

This blog post explains why coverage percentage dropped to 0% and how to get the test coverage back in the sonarcloud reports.

The explanation assumes familiarity with apache maven.

The reason coverage suddenly dropped to 0%, was that sonarcloud stopped retrieving coverage information from the target/jacoco.exec files.

The way to get test coverage back in sonarcloud is to first let jacoco-maven-plugin generate an XML format test coverage file from the jacoco.exec file(s), and then point sonar-scanner-maven to the XML file instead of the default (which still are the jacoco.exec files).

Before explaining how to fix the build, I’ll recap how thing used to work.

The .travis.yml build command for sonar analysis at the time the test coverage started dropping to 0%, was:

Listing 1.

mvn -B clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar

which means:

  1. clean means delete all target directories in the current project and modules
  2. org.jacoco:jacoco-maven-plugin:prepare-agent means “set up jacoco to be the maven test runner container”
  3. package means that maven runs all of the java build steps up to and including test, and since jacoco is the test runner, leave a jacoco.exec file in the target directory
  4. sonar:sonar invokes the sonar-scanner-maven maven plugin to extract information from the reactor build and send to the sonar instance config points it to.

By default sonar-scanner-maven will look for jacoco.exec files, and send them to the sonar instance, whether the sonar instance understands them or not.

Note: Somewhat confusingly for local debugging, the downloadable community edition sonarqube distributions did not drop support for the jacoco.exec files at the same time, in fact a release I downloaded yesterday still supported jacoco.exec.

Getting sonarcloud test coverage back in a single-module maven project

In a single module maven project, you will need to the the following things:

  1. To generate a coverage XML file, add a maven configuration for the jacoco-maven-plugin binding the plugin’s report goal to the maven verify phase (the phase is picked because it is after the tests have run and the target/jacoco.exec file is created):

    Listing 1.

    <project>
     <build>
      <plugins>
       <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.5</version>
        <executions>
         <execution>
          <id>report</id>
          <goals>
           <goal>report</goal>
          </goals>
          <phase>verify</phase>
         </execution>
        </executions>
       </plugin>
      </plugins>
     </build>
    </project>
    
  2. In the .travis.yml file (or your equivalent), in the sonar generating command line, switch from “package” to “verify” to ensure that the coverage XML file is generated with content, by running it after the target/jacoco.exe file has been created:

    Listing 1.

    script:
        - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent verify sonar:sonar
    

Getting sonarcloud test coverage back in a multi-module maven project

A multi-module maven project uses a slightly different approach: an aggregate coverage file is created by the maven modules’ jacoco.exec file and sonar-scanner-maven is pointed to this file.

The changes to get coverage back in a multi-module project, are:

  1. Add a new maven module just for aggregating the coverage results, with a pom binding the jacoco-maven-plugin aggregate-report to the verify phase, and a dependency list containing the modules that aggregate-report should scan for target/jacoco.exec files

    Listing 1.

    <project>
     <parent>
      <groupId>no.priv.bang.demos.jerseyinkaraf</groupId>
      <artifactId>jerseyinkaraf</artifactId>
      <version>1.0.0-SNAPSHOT</version>
     </parent>
     <artifactId>jacoco-coverage-report</artifactId>
     <dependencies>
      <dependency>
       <groupId>no.priv.bang.demos.jerseyinkaraf</groupId>
       <artifactId>jerseyinkaraf.servicedef</artifactId>
       <version>${project.version}</version>
      </dependency>
      <dependency>
       <artifactId>jerseyinkaraf.services</artifactId>
       <groupId>no.priv.bang.demos.jerseyinkaraf</groupId>
       <version>${project.version}</version>
      </dependency>
      <dependency>
       <groupId>no.priv.bang.demos.jerseyinkaraf</groupId>
       <artifactId>jerseyinkaraf.webapi</artifactId>
       <version>${project.version}</version>
      </dependency>
      <dependency>
       <groupId>no.priv.bang.demos.jerseyinkaraf</groupId>
       <artifactId>jerseyinkaraf.webgui</artifactId>
       <version>${project.version}</version>
      </dependency>
     </dependencies>
     <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>
    </project>
    
  2. Add a maven property to the top POM telling the sonar-scanner-maven plugin where to look for the aggregate coverage XML file (deep in the structure of the new module creating the aggregate coverage file):

    Listing 1.

    <project>
     <properties>
      <sonar.coverage.jacoco.xmlReportPaths>${project.basedir}/../jacoco-coverage-report/target/site/jacoco-aggregate/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
     </properties>
    </project>
    
  3. Switch “package” in the sonar command line in .travis.yml, to at least the phase running sonar-scanner-maven plugin (i.e. “verify”), but later phases such as “install”, works as well:

    Listing 1.

    script:
        - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent verify sonar:sonar