在Gradle multi模块项目

时间:2017-07-07 21:55:51

标签: java gradle sonarqube code-coverage jacoco

我目前正在使用具有良好覆盖率的gradle multi模块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服务器上的projekt时,实施项目的覆盖范围是正确测量的,但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:

  1. JaCoCo(codeCoverageReport-Task)
    • 73%教学报道
    • 91%的分行覆盖率
  2. 声纳
    • 43.1%线路覆盖率(在计算中仅考虑约30%的线路!)
    • 82.1%条件覆盖率(仅涵盖约20%的条件!)
  3. 因此声纳的覆盖率结果不可用。我已经阅读了一篇帖子,宣布" sonar.jacoco.reportPaths" -parameter以sonar 6.2开头,我认为java-analyzer 4.4或者......像那样。将此参数添加到我的gradle构建脚本时,脚本不再编译。通过声纳项目管理将jacoco .exec文件添加到声纳时没有任何变化。

    如果有办法管理声纳来计算正确的覆盖范围,那就太好了。

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

Thx @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%
  • Uncovered Lines 36
  • 未覆盖的条件11
  • 线路覆盖433
  • 单元测试1,114
  • 单元测试错误0
  • 单元测试失败0
  • Skipped Unit Tests 0
  • 单元测试成功率(%)100.0%
  • 单元测试持续时间4s

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

答案 2 :(得分:0)

我不确定我理解为什么只有一些项目才能让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))
    }
}