SonarQube:与JaCoCo的多模块gradle项目的覆盖范围不完整

时间:2017-01-19 09:08:17

标签: gradle sonarqube jacoco

我正在构建一个已经在分析我的Java 8 / Gradle 3.3项目的SonarQube 6.2服务器。将JaCoCo添加到多模块gradle项目时,我意识到SonarQube正在“按模块”测量代码覆盖率:

如果某个类位于模块A中且该类的测试位于模块B中,则SonarQube会将该类未涵盖

我想测量所有模块的代码覆盖率,而不是基于每个模块。我如何实现这一目标?

有许多类似的问题,但没有有用的答案,虽然这种情况对我来说似乎很常见。例如Jenkins默认情况下这样做。

我决定建立blueprint on github以澄清问题。

build.gradle

组成
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.gradle为空。

它包含3个类:TestedInModATestedInModATestTestedViaModB

modB/build.gradle只声明对modA的依赖:

dependencies { compile project(':modA') }

它只包含一个类TestedViaModBTest,测试位于TestedViaModB的类modA

我的(私有)Jenkins实例显示包含的两个类的100%覆盖率,而SonarQube表示仅涵盖了类TestedInModA(在其自己的模块中测试)。

如何修改构建过程以查看SonarQube中的“跨模块覆盖”?

我很想更新我的项目,以便此问题的未来访问者可以找到一个有效的例子。

我的工作解决方案(感谢@Godin)

  1. 将以下内容添加到subprojects闭包

    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. 添加

    task cleanJacoco(dependsOn: 'clean') {  delete "$buildDir/jacoco" }
    
  3. 外面 subprojects关闭。

2 个答案:

答案 0 :(得分:6)

执行构建时,JaCoCo Gradle Plugin将生成modA/build/jacoco/test.execmodB/build/jacoco/test.exe,其中包含分别在modAmodB中执行测试的信息。 SonarQube分别对模块进行分析,因此在分析文件modA的{​​{1}}时,它只能看到TestedViaModB

跨越边界的最常见技巧 - 将所有覆盖信息收集到单个位置。这可以通过以下方式完成 JaCoCo Gralde插件

  • 通过更改位置 - 请参阅destinationFiledestPath(因为信息已附加到modA/build/jacoco/test.exec文件,请不要忘记在构建之前删除此单个位置,否则它将累积来自不同构建的信息,而不仅仅来自不同的模块),

  • 将所有文件合并为一个文件 - 请参阅JacocoMerge task。然后将此单个位置指定为SonarQube sonar.jacoco.reportPath

另一个技巧:SonarQube 6.2 with Java Plugin 4.4 supports property sonar.jacoco.reportPaths允许指定多个位置。

答案 1 :(得分:0)

如果您对使用sonar.jacoco.reportPaths的解决方案感兴趣(请参阅Godin的答案),请查看此gradle代码:

tasks.getByName('sonarqube') {
    doFirst {
        // lazy initialize the property to collect all coverage files
        def jacocoFilesFromSubprojects = subprojects.findAll {it.plugins.hasPlugin('jacoco')}
                .collect {it.tasks.withType(Test)}.flatten().collect {it.jacoco.destinationFile}

        sonarqube.properties {
            property "sonar.jacoco.reportPaths", jacocoFilesFromSubprojects.join(',')
        }
    }
}

这将收集所有coverage二进制文件,并将它们设置为sonar属性的逗号分隔列表。考虑应用了jacoco并配置了jacoco目标文件的子项目的所有测试任务。