【问题标题】:Cross-module code coverage with jacoco and gradle multi-module projectjacoco 和 gradle 多模块项目的跨模块代码覆盖率
【发布时间】:2017-01-02 15:58:02
【问题描述】:

我们使用 gradle 3.3 和 jacoco 工具版本 0.7.6.201602180812。 我们有一个这样的 gradle 多项目:

  • 父级
    • 产品1
    • 产品2
    • 产品3
    • int-test

我们使用单元测试来测试项目源代码,并在所有生成 test.exec 文件的子项目上使用 jacoco。我们在 int-test 项目中有额外的集成测试,将 jacoco 结果添加到 int-test 项目中的 test-exec 中。我们在父项目上使用 sonarqube gradle 插件(2.2.1)来收集 SonarQube 服务器 v6.2 的所有内容。

在他们自己的项目中测试源代码的测试一切运行良好:代码覆盖率在 jacoco 报告以及 SonarQube 上进行测量。 只有 prod-projects(单个进程)中源的集成测试(int-test 项目)覆盖率既不在测试项目的覆盖率报告中,也不在类项目的覆盖率报告中测量。 可能需要以某种方式组合顶级项目的覆盖率数据 - 有人知道该怎么做吗?最好的情况是 SonarQube 仍然显示单个模块级别的覆盖率。

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

  • 运行所有测试
  • 在所有子项目中生成 jacoco/test.exec 和 test-results/test/... 文件
  • 在 sonarqube 中解析这些
  • 为在自己的项目中测试源的测试正确测量覆盖率
  • 在另一个项目中测试源的测试缺少覆盖率

【问题讨论】:

  • 您的项目结构不清楚。你可能想edit你的问题来扩展它。
  • 感谢您的输入,我试图让我的布局更清晰。
  • 您的示例项目是否仍可通过某种方式访问​​?如果我了解 JacocoSensor 在 Sonar 中的工作原理,它要求您在 int-test 模块的类路径中提供可用的类文件。来自 prod1、prod2 等的含义类。
  • @dbalakirev 我刚才删除了这个例子,因为我的问题已经解决了。

标签: gradle sonarqube jacoco multi-module


【解决方案1】:

如 cmets 中所述,您必须首先使用 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 插件的孵化类)的自定义任务,它将所有项目 (executionData) 中的所有测试覆盖率报告合并到我们的 allTestCoverageFile 中。

如果您使用的是 6.2 之前的 SonarQube 版本,请使用 sonar.jacoco.reportPath 属性

【讨论】:

  • 嗨 Cristian,sonar.jacoco.reportPaths 和 sonar.jacoco.reportPath 两个属性有什么区别(没有尾随's')?您写道,为每个子项目全局设置此属性很重要……但这是两个不同的属性!怎么回事……?!
  • docs.sonarqube.org/display/PLUG/… 如果您使用的是 6.2 之前的 SonarQube 版本,请使用 sonar.jacoco.reportPath 属性
  • 我明白你的意思。我有一个错字。我会解决的。
  • @Cristian 我可以要你的电子邮件吗?
  • sonarqube.com 目前已关闭,但我清楚地记得读过最新的 Sonarqube 插件“org.sonarqube:2.7”不支持二进制 .exec 文件。我还记得阅读 sonarqube 插件必须仅从根项目运行(因此从子项目闭包运行它的强调指令似乎已过时)。这意味着上述解决方案不再适用。 org.sonarqube:2.7 插件似乎支持以逗号分隔的 XML 报告列表,用于 sonar.coverage.jacoco.xmlReportPaths 属性,但是,所以我正在查看一些 Groovy 脚本来生成它。
【解决方案2】:

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

只谈 Gradle:看看

【讨论】:

  • 感谢您的输入,我将尝试 jacocoMerge 任务。对于 SonarQube:它可以很好地收集个人数据,但不能收集跨项目数据。
  • @MichaelZöller 再次谈到 SonarQube:将数据收集到单个文件“jacoco.exec”中,例如在相对于每个模块或跨模块相同的任何其他文件的“../jacoco.exec”中,通过将“sonar.jacoco.reportPath”设置为“../jacoco.exec”来指示 SonarQube 在分析期间使用此位置”。如果这没有帮助,那么请努力更准确地描述您的问题并提供示例(请参阅stackoverflow.com/help/mcve)。
  • 我终于花时间使用了单一的 jacoco.exec 方法。这是有效的。此外,还必须将 junit-test 结果复制到一个位置,以便在 SonarQube 中获得正确的测试计数。我已经在 github 上对项目进行了所需的任务。用于 jacoco 路径集合的新 SQ 6.2 属性似乎不适用于 sonar-gradle-plugin 版本 2.2.1。
【解决方案3】:
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。在此处查看文档

【讨论】:

  • 我得到“无法为 org.gradle.api.Project 类型的项目 ':app' 获取未知属性 'test'。”我将此逻辑提取到 jacoco.gradle 文件中,但这对我来说并不重要。
猜你喜欢
  • 2014-10-03
  • 2020-05-17
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 2020-08-13
  • 2017-06-24
  • 2020-10-29
  • 2015-01-10
相关资源
最近更新 更多