Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SonarQube: Coverage incomplete on multimodule gradle project with JaCoCo

I am building a SonarQube 6.2 server which is already analyzing my Java 8/Gradle 3.3 projects. When adding JaCoCo to a multimodule gradle project, I realized that SonarQube is measuring code coverage on a "per-module" basis:

If a class is located in module A and a test for this class is located in module B, SonarQube figures the class is not covered.

I want to measure code coverage across all modules, not on a per module basis. How do I achieve this?

There are lots of similar questions but no helpful answers, although the situation seems quite common to me. Jenkins for example does that per default.

I decided to build a blueprint on github to clarify the issue.

The main build.gradle consists of

plugins { id "org.sonarqube" version "2.2.1" }

subprojects {
    apply plugin: 'java'
    apply plugin: 'jacoco'

    repositories { mavenCentral() }
    dependencies { testCompile "junit:junit:4.12" }
}

modA/build.gradleis empty.

It contains 3 classes: TestedInModA, TestedInModATest and TestedViaModB.

modB/build.gradlejust declares a dependency to modA:

dependencies { compile project(':modA') }

It contains just one class: TestedViaModBTest, testing the class TestedViaModB located in modA.

My (private) Jenkins instance shows 100% coverage for the two classes included while SonarQube says only the class TestedInModA (which is tested in its own module) is covered.

How can I modify my build process to see "cross-module coverage" in SonarQube?

I would love to update my project so future visitors to this question can find a working example.

My working solution (thanks @Godin)

  1. add the following to the subprojects closure

    tasks.withType(Test) {
        // redirect all coverage data to one file
        // ... needs cleaning the data prior to the build to avoid accumulating coverage data of different runs.
        // see `task cleanJacoco`
        jacoco {
            destinationFile = file("$rootProject.buildDir/jacoco/test.exec")
        }
    }
    
  2. add

    task cleanJacoco(dependsOn: 'clean') {  delete "$buildDir/jacoco" }
    

outside the subprojects closure.

like image 592
sk_dev Avatar asked Jan 19 '17 09:01

sk_dev


1 Answers

When you perform build JaCoCo Gradle Plugin will produce modA/build/jacoco/test.exec and modB/build/jacoco/test.exe that contain information about execution of tests in modA and modB respectively. SonarQube performs analysis of modules separately, so during analysis of modA for the file TestedViaModB it sees only modA/build/jacoco/test.exec.

Most common trick to cross boundaries - is to collect all coverage information into single location. This can be done with JaCoCo Gralde Plugin

  • either by changing location - see destinationFile and destPath ( since information is appended to exec file, don't forget to remove this single location prior to build, otherwise it will be accumulating information from different builds and not just from different modules ),

  • either by merging all files into single one - see JacocoMerge task. And then specify this single location to SonarQube as sonar.jacoco.reportPath.

Another trick: SonarQube 6.2 with Java Plugin 4.4 supports property sonar.jacoco.reportPaths allowing to specify multiple locations.

like image 111
Godin Avatar answered Nov 11 '22 15:11

Godin