【问题标题】:Wrong coverage calculation in sonarqube 6.2 on Gradle multi module projectGradle 多模块项目的 sonarqube 6.2 中的错误覆盖率计算
【发布时间】:2017-07-07 21:55:51
【问题描述】:

我目前正在使用具有良好覆盖率的 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
}

我目前的结果如下:

贾可可:

  1. JaCoCo(codeCoverageReport-任务)
    • 73% 指令覆盖率
    • 91% 的分支机构覆盖率
  2. 声纳
    • 43.1% 的线路覆盖率(计算中仅考虑约 30% 的线路!)
    • 82.1% 条件覆盖(仅覆盖约 20% 条件!)

所以声纳中的覆盖结果不可用。我已经阅读了一篇宣布从 sonar 6.2 开始的“sonar.jacoco.reportPaths”参数的帖子,我认为 java-analyzer 4.4 或 sth。像那样。将此参数添加到我的 gradle 构建脚本时,该脚本不再编译。通过声纳项目管理将 jacoco .exec 文件添加到声纳时,没有任何变化。

如果有一种方法可以管理声纳以计算正确的覆盖范围,那就太好了。

【问题讨论】:

    标签: java gradle sonarqube code-coverage jacoco


    【解决方案1】:

    谢谢@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%
    • 单元测试持续时间 4 秒

    希望这对你们中的一些人有所帮助... ;)

    【讨论】:

    • 有一个JacocoMerge任务可以将多个exec文件合并为一个
    • 接下来几天会再试一次。在第一次测试的最后几天没有正常工作。
    • 在 gradle 测试中有一个示例 here 将多个 Test 任务合并到单个项目中并生成合并报告。您可以调整您的多项目设置
    • 感谢现在有时间修改我的项目。覆盖结果是一样的,但在我看来,它更干净,也更少胶带...... ;) 更新接受的答案。
    • 您应该在调用分析之前将更改推送到存储库。 JaCoCo-Merge 解决方案更简洁,但不像其他解决方案那样获得跨项目覆盖。降低了 api 和 test-utils 项目的覆盖率 -> 91% 覆盖率 -> 60%(仅使用 impl 项目,它是唯一具有测试的项目)。原因很简单,只有 impl 项目有 .exec 文件...
    【解决方案2】:

    如果您的测试与您想要覆盖报告的源位于不同的项目中,那么您需要设置additionalSourceDirsadditionalClassDirs。例如:

    evaluationDependsOn ':foo' 
    task codeCoverageReport(type: JacocoReport) {
        additionalSourceDirs.add project(':foo').sourceSets.main.java.sourceDirectories
        additionalClassDirs.add project(':foo').sourceSets.main.output.classesDirs
        // etc
    } 
    

    【讨论】:

    • 谢谢。我会检查一下。希望我能让这个工作。将尽快回复结果。
    • 我能够创建一个您的想法有效的示例。对我来说最好的解决方案是下面的答案......
    • @The_Gentleman “下面的答案”没有帮助。您应该 A. 为正确答案投票 B. 指定写答案的人的用户名,以便稍后来的人知道您的意思。
    【解决方案3】:

    我不确定我是否理解为什么只有一些项目有 jacoco 而其他项目没有。您可以使用 Gradle 丰富的 API(例如 TaskCollectionProject)来动态查找它们。

    例如:

    [':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))
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2017-01-10
      • 1970-01-01
      • 2021-08-01
      • 2017-08-17
      • 2017-06-03
      • 1970-01-01
      • 2019-08-20
      • 1970-01-01
      • 2017-01-03
      相关资源
      最近更新 更多