Cross-module jacoco 和 gradle multi-module 项目的代码覆盖率

Cross-module code coverage with jacoco and gradle multi-module project

我们使用 gradle 3.3 和 jacoco 工具版本 0.7.6.201602180812。 我们有一个 gradle multi-project 这样的:

我们使用 unit-tests 测试所有 child-projects 生成的 test.exec 文件的项目源和 jacoco。我们在 int-test 项目中有额外的 integration-tests,将 jacoco 结果添加到 int-test 项目中的 test-exec。我们在 parent 项目上使用 sonarqube gradle 插件 (2.2.1) 来收集 SonarQube 服务器 v6.2 的所有内容。

在他们自己的项目中测试源的测试一切都运行良好:代码覆盖率在 jacoco 报告和 SonarQube 上测量。

只有集成测试(int-test 项目)对 prod-projects(单个进程)中源的覆盖率未在带有测试的项目和带有 [=70 的项目的覆盖率报告中测量=].

可能需要以某种方式组合顶级项目的覆盖率数据——有人知道该怎么做吗?充其量 SonarQube 仍然显示单个模块级别的覆盖范围。

编辑 这是一个小测试项目:https://github.com/MichaelZett/coveragetest 运行宁
'build smokeTest sonarqube'导致:

关于 SonarQube:您可以通过在所有模块中对 jacoco.exec 使用相同的位置来获取汇总报告。确保在构建之前删除该文件并将其附加到所有模块中。

只谈Gradle:看看

如评论中所述,您必须首先 merge the Jacoco execution 数据,然后告诉 sonarqube 使用该数据,而不是每个子模块生成的单独 exec 文件。

我在这里添加了一个例子,因为接受的答案中提供的链接有点误导。他们中的大多数为您提供了不同的解决方法来合并 Jacoco 报告,而不是合并执行数据,这正是您想要的。

这是它的样子:

def allTestCoverageFile = "$buildDir/jacoco/allTestCoverage.exec"

sonarqube {
    properties {
        property "sonar.projectKey", "your.org:YourProject"
        property "sonar.projectName", "YourProject"
        property "sonar.jacoco.reportPaths", allTestCoverageFile
    }
}

task jacocoMergeTest(type: JacocoMerge) {
    destinationFile = file(allTestCoverageFile)
    executionData = project.fileTree(dir: '.', include:'**/build/jacoco/test.exec')
}

task jacocoMerge(dependsOn: ['jacocoMergeTest']) {
    // used to run the other merge tasks
}

subprojects {
    sonarqube {
        properties {
            property "sonar.jacoco.reportPaths", allTestCoverageFile
        }
    }
}

简而言之:

  • 首先,我们为测试报告 (allTestCoverageFile) 定义一个全局覆盖率文件输出。
  • 然后我们需要告诉 Sonarqube 使用该文件(使用 sonar.jacoco.reportPaths)。但请注意,我们还必须在子项目关闭时执行此操作。这是非常重要的。不要错过。
  • 最后,我们创建了一个扩展自 JacocoMerge 的自定义任务(来自 Jacoco 插件的孵化 class),它合并了所有项目的所有测试覆盖率报告(executionData ) 到我们的 allTestCoverageFile.

If you are using a version of SonarQube prior to 6.2 please use sonar.jacoco.reportPath property

subprojects {
    apply(plugin: 'org.jetbrains.kotlin.jvm')

    repositories {
        jcenter()
        mavenCentral()
   }
}

task codeCoverageReport(type: JacocoReport) {

    // Gather execution data from all subprojects
    executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec")

    // Add all relevant sourcesets from the subprojects
    subprojects.each {
        sourceSets it.sourceSets.main
    }

    reports {
        xml.enabled true
        html.enabled true
        csv.enabled false
    }
}

// always run the tests before generating the report
codeCoverageReport.dependsOn {
    subprojects*.test
}

sonarqube {
    properties {
        property "sonar.projectKey", "your_project_key"
        property "sonar.verbose", true
        property "sonar.projectName", "Your project name"
        property "sonar.coverage.jacoco.xmlReportPaths", "${rootDir}/build/reports/jacoco/codeCoverageReport/codeCoverageReport.xml"
    }
}

运行 测试覆盖率的命令:

./gradlew codeCoverageReport
./gradlew sonarqube -x test (test is excluded since already run and sonarqube by default executes test)

使它起作用的两点需要注意:

  1. 为了使所有模块的源集可用,遍历子项目和累积源集是可行的。 subprojects.sourceSets.main.allSource.srcDirs 无效。
  2. sonar.jacoco.reportPaths 弃用 。我们需要使用sonar.coverage.jacoco.xmlReportPaths。在此处查看文档