Gradle 多模块项目中 sonarqube 6.2 的覆盖率计算错误
Wrong coverage calculation in sonarqube 6.2 on Gradle multi module project
我目前正在使用覆盖面广的 gradle 多模块 java 项目和带有 sonarJava 插件 4.10.0.1026 的 sonarqube 6.2。我正在使用 Gradle 4.0.1、sonarqube 插件 2.5 和 jacoco 0.7.9!密码是java 8.
由于 API 驱动开发,API 测试在 API 项目中编写为抽象测试,并从为测试提供构造函数的实施项目中调用。
在 sonarqube 服务器上分析项目时,正确测量了实施项目的覆盖率,但 IMPL 项目测试中包含的 API 项目覆盖率为 0.0%。这些项目的覆盖率结果将被忽略。
当简单地使用 jacoco 插件时,我能够得到相同的行为。在做了一些研究之后,我找到了一个解决方案来获得正确的 jacoco 报告:
task codeCoverageReport(type: JacocoReport) {
description "Creates a unified JaCoCo test report for the project."
// Gather execution data from all subprojects
// (change this if you e.g. want to calculate unit test/integration test coverage separately)
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
html.destination file("${buildDir}/reports/jacoco")
csv.enabled false
}
}
// always run the tests before generating the report
codeCoverageReport.dependsOn {
subprojects*.test
}
我目前的结果如下:
JaCoCo:
- JaCoCo(codeCoverageReport-任务)
- 73% 的指令覆盖率
- 91% 的分支机构覆盖率
- 声纳
- 43.1% 线路覆盖率(计算中仅考虑 ~30% 线路!)
- 82.1% 的条件覆盖率(仅覆盖约 20% 的条件!)
所以声纳中的覆盖结果不可用。我读过 post 宣布 "sonar.jacoco.reportPaths"-parameter 从声纳 6.2 开始,我认为 java-analyzer 4.4 或某物。像那样。将此参数添加到我的 gradle 构建脚本时,该脚本不再编译。通过声纳项目管理将 jacoco .exec 文件添加到声纳时,没有任何变化。
如果有一种方法可以管理声纳来计算正确的覆盖范围,那就太好了。
如果您的测试与您想要覆盖率报告的源位于不同的项目中,那么您需要设置 additionalSourceDirs
和 additionalClassDirs
。例如:
evaluationDependsOn ':foo'
task codeCoverageReport(type: JacocoReport) {
additionalSourceDirs.add project(':foo').sourceSets.main.java.sourceDirectories
additionalClassDirs.add project(':foo').sourceSets.main.output.classesDirs
// etc
}
谢谢@Lance Java!他促使我采用比下面的解决方案更清洁的解决方案。如果所有子项目都有 jacoco 报告,这也适用。如果像我一样只有几个项目的报告,那么原来的解决方案似乎效果更好。
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
}
}
}
[...]
task jacocoMerge( type: JacocoMerge ) {
dependsOn( subprojects.jacocoTestReport.dependsOn )
mustRunAfter( subprojects.jacocoTestReport.mustRunAfter )
destinationFile = file( "${buildDir}/jacoco/mergedTests.exec" )
executionData = files( subprojects.jacocoTestReport.executionData )
.filter { jacocoReportFile -> jacocoReportFile.exists() }
}
tasks.sonarqube.dependsOn jacocoMerge
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
原回答:
设法将正确的覆盖数据传输到声纳需要一些时间。有多个问题需要解决。有时 Sonar 无法跟踪 类 中的 jacoco 更改,因此测试需要参数:
append=true
这并没有完成所有工作。收集跨项目覆盖率仍然存在问题。因此,最好的解决方案是强制 jacoco 将覆盖率数据写入单个 .exec 文件并将其交给声纳。
最终解决方案如下所示:
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
destinationFile = file( "${rootProject.buildDir}/jacoco/jacocoTest.exec" )
}
}
}
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
现在声纳为我的项目提供了正确的覆盖数据。添加一些额外的测试后,结果如下:
- 总覆盖率 91.6%
- 线路覆盖率91.7%
- 条件覆盖率 91.3%
- 未覆盖的第 36 行
- 未覆盖条件 11
- 覆盖 433 行
- 单元测试 1,114
- 单元测试错误 0
- 单元测试失败 0
- 跳过单元测试 0
- 单元测试成功率 (%) 100.0%
- 单元测试时长 4s
希望这对你们中的一些人有所帮助...;)
我不确定我是否理解为什么只有一些项目有 jacoco 而其他项目没有。您可以使用Gradle的富人API(例如TaskCollection and Project)来动态找到它们。
例如:
[':project1', ':project3', ':project5'].each {
project(it) {
apply plugin: 'java'
apply plugin: 'jacoco'
}
}
project(':merger') {
Collection<Project> jacocoProjects = allprojects.findAll { it.plugins.hasPlugin('jacoco' }
evaluationDependsOn jacocoProjects
task jacocoMerge(type: JacocoMerge) {
dependsOn jacocoProjects*.tasks.withType(Test)
executionData jacocoProjects*.tasks.withType(Test)
}
task mergedReport(type: JacocoReport) {
dependsOn jacocoMerge
executionData jacocoMerge.destinationFile
sourceDirectories.add(files(jacocoProjects*.sourceSets*.java.srcDirs))
classDirectories.add(files(jacocoProjects*.sourceSets*.output.classesDir))
}
}
我目前正在使用覆盖面广的 gradle 多模块 java 项目和带有 sonarJava 插件 4.10.0.1026 的 sonarqube 6.2。我正在使用 Gradle 4.0.1、sonarqube 插件 2.5 和 jacoco 0.7.9!密码是java 8.
由于 API 驱动开发,API 测试在 API 项目中编写为抽象测试,并从为测试提供构造函数的实施项目中调用。
在 sonarqube 服务器上分析项目时,正确测量了实施项目的覆盖率,但 IMPL 项目测试中包含的 API 项目覆盖率为 0.0%。这些项目的覆盖率结果将被忽略。
当简单地使用 jacoco 插件时,我能够得到相同的行为。在做了一些研究之后,我找到了一个解决方案来获得正确的 jacoco 报告:
task codeCoverageReport(type: JacocoReport) {
description "Creates a unified JaCoCo test report for the project."
// Gather execution data from all subprojects
// (change this if you e.g. want to calculate unit test/integration test coverage separately)
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
html.destination file("${buildDir}/reports/jacoco")
csv.enabled false
}
}
// always run the tests before generating the report
codeCoverageReport.dependsOn {
subprojects*.test
}
我目前的结果如下:
JaCoCo:
- JaCoCo(codeCoverageReport-任务)
- 73% 的指令覆盖率
- 91% 的分支机构覆盖率
- 声纳
- 43.1% 线路覆盖率(计算中仅考虑 ~30% 线路!)
- 82.1% 的条件覆盖率(仅覆盖约 20% 的条件!)
所以声纳中的覆盖结果不可用。我读过 post 宣布 "sonar.jacoco.reportPaths"-parameter 从声纳 6.2 开始,我认为 java-analyzer 4.4 或某物。像那样。将此参数添加到我的 gradle 构建脚本时,该脚本不再编译。通过声纳项目管理将 jacoco .exec 文件添加到声纳时,没有任何变化。
如果有一种方法可以管理声纳来计算正确的覆盖范围,那就太好了。
如果您的测试与您想要覆盖率报告的源位于不同的项目中,那么您需要设置 additionalSourceDirs
和 additionalClassDirs
。例如:
evaluationDependsOn ':foo'
task codeCoverageReport(type: JacocoReport) {
additionalSourceDirs.add project(':foo').sourceSets.main.java.sourceDirectories
additionalClassDirs.add project(':foo').sourceSets.main.output.classesDirs
// etc
}
谢谢@Lance Java!他促使我采用比下面的解决方案更清洁的解决方案。如果所有子项目都有 jacoco 报告,这也适用。如果像我一样只有几个项目的报告,那么原来的解决方案似乎效果更好。
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
}
}
}
[...]
task jacocoMerge( type: JacocoMerge ) {
dependsOn( subprojects.jacocoTestReport.dependsOn )
mustRunAfter( subprojects.jacocoTestReport.mustRunAfter )
destinationFile = file( "${buildDir}/jacoco/mergedTests.exec" )
executionData = files( subprojects.jacocoTestReport.executionData )
.filter { jacocoReportFile -> jacocoReportFile.exists() }
}
tasks.sonarqube.dependsOn jacocoMerge
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
原回答:
设法将正确的覆盖数据传输到声纳需要一些时间。有多个问题需要解决。有时 Sonar 无法跟踪 类 中的 jacoco 更改,因此测试需要参数:
append=true
这并没有完成所有工作。收集跨项目覆盖率仍然存在问题。因此,最好的解决方案是强制 jacoco 将覆盖率数据写入单个 .exec 文件并将其交给声纳。
最终解决方案如下所示:
apply plugin: 'base'
apply plugin: 'org.sonarqube'
[...]
allprojects {
apply plugin: 'java'
apply plugin: "jacoco"
[...]
test {
[...]
jacoco {
append=true
destinationFile = file( "${rootProject.buildDir}/jacoco/jacocoTest.exec" )
}
}
}
[...]
sonarqube {
properties {
[...]
property "sonar.jacoco.reportPath", "${buildDir}/jacoco/*.exec"
}
}
现在声纳为我的项目提供了正确的覆盖数据。添加一些额外的测试后,结果如下:
- 总覆盖率 91.6%
- 线路覆盖率91.7%
- 条件覆盖率 91.3%
- 未覆盖的第 36 行
- 未覆盖条件 11
- 覆盖 433 行
- 单元测试 1,114
- 单元测试错误 0
- 单元测试失败 0
- 跳过单元测试 0
- 单元测试成功率 (%) 100.0%
- 单元测试时长 4s
希望这对你们中的一些人有所帮助...;)
我不确定我是否理解为什么只有一些项目有 jacoco 而其他项目没有。您可以使用Gradle的富人API(例如TaskCollection and Project)来动态找到它们。
例如:
[':project1', ':project3', ':project5'].each {
project(it) {
apply plugin: 'java'
apply plugin: 'jacoco'
}
}
project(':merger') {
Collection<Project> jacocoProjects = allprojects.findAll { it.plugins.hasPlugin('jacoco' }
evaluationDependsOn jacocoProjects
task jacocoMerge(type: JacocoMerge) {
dependsOn jacocoProjects*.tasks.withType(Test)
executionData jacocoProjects*.tasks.withType(Test)
}
task mergedReport(type: JacocoReport) {
dependsOn jacocoMerge
executionData jacocoMerge.destinationFile
sourceDirectories.add(files(jacocoProjects*.sourceSets*.java.srcDirs))
classDirectories.add(files(jacocoProjects*.sourceSets*.output.classesDir))
}
}