Scenario
有如下工程结构:
rootProject
|
├── subProject1
| |
| └── build.gradle
|
├── subproject2
| |
| └── build.gradle
|
└── build.gradle
在rootProject/build.gradle
里引入并使用sonarqube plugin (sonarqube plugin本身具有多工程分析的能力,因此只需在主工程中引入):
plugins {
id "org.sonarqube" version "2.7"
}
如果test case用了TestNG,请指明
test {
useTestNG()
}
Jacoco会根据test的配置,自动去寻找运行test后所生成的 *.exec文件,用于类型为JacocoReport(如jacocoTestReport)的task生成相应的测试报告。
关于 jacoco 插件的使用,请参考 https://docs.gradle.org/current/userguide/jacoco_plugin.html, 引入 java 插件的目的是让相应的 project自动生成 jacocoTestReport task, 而 jacocoTestReport task 的主要作用就是用来生成覆盖率测试报告,以及生成用于 sonarqube 分析的 XML 文件
现在要将整个工程的测试报告上传到SonarQube,有以下两种方法。
Solutions
方法一
合并所有工程生成的*.exec
文件,jacocoTestReport利用合并后的文件来生成sonar所需的xml文件,因此合并的代码应该放置在rootProject的build.gradle
里,具体代码如下:
// Merge jacoco coverage results and generate reports in xml and html format
jacocoTestReport {
sourceDirectories = files()
classDirectories = files()
executionData = files()
reports {
html.enabled = true
xml.enabled = true
csv.enabled = false
}
// Work-around to allow us to build list of executionData files in doFirst
onlyIf = {
true
}
/*
* Builds list of source dirs, class dirs, and executionData files
* when task is run, not at script evaluation time
*/
doFirst {
subprojects.findAll {
it.pluginManager.hasPlugin('jacoco')
}.each { subproject ->
additionalSourceDirs files((Set<File>) subproject.sourceSets.main.allJava.srcDirs)
additionalClassDirs ((FileCollection) subproject.sourceSets.main.output)
executionData subproject.tasks.jacocoTestReport.executionData
}
executionData = files(executionData.findAll {
it.exists()
})
}
}
上述task会在rootProject里生成build目录,而build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
正是sonarqube分析覆盖率时所需要的,因此在rootProject里配置
// Configure sonar to take the merged xml rseult
sonarqube {
properties {
property "sonar.coverage.jacoco.xmlReportPaths", jacocoTestReport.reports.xml.destination
}
}
tasks['sonarqube'].dependsOn(jacocoTestReport)
即可。
方法二
利用sonarqube插件可以自动分析主工程与子工程的特性,每个工程的sonarqube都指定sonar.coverage.jacoco.xmlReportPaths
, 最终会汇总在SonarQube server上。具体步骤如下:
-
在所有工程里都引入和使用jacoco与java插件,或者在rootProject里使用如下代码:
allprojects { apply plugin: 'java' apply plugin: 'jacoco' }
目的是让所有工程都有jacocoTestReport task。
-
每个工程都将jacocoTestReport的xml功能打开:
jacocoTestReport { reports { html.enabled = true xml.enabled = true csv.enabled = false } }
-
然后在每个工程里加上
sonarqube { properties { property "sonar.coverage.jacoco.xmlReportPaths", jacocoTestReport.reports.xml.destination } }
关于如何排除需要分析的文件
如果你最终的测试报告就是jacocoTestReport生成的,则可以通过
jacocoTestReport {
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it, exclude: 'com/blah/**')
})
}
}
来排除不必要的文件,可以参考 https://stackoverflow.com/questions/29887805/filter-jacoco-coverage-reports-with-gradle
但如果你的测试报告最终是由SonqrQube来分析的,那么需要配置sonar.exclusions来排除,例如
sonarqube {
properties {
property "sonar.exclusions", [
'**/com/siemens/mindsphere/actuator/**/*',
'**/com/siemens/mindsphere/config/*Config.java'
]
}
}
对于每个子工程想要排除文件,请将上述配置加到相应的子工程的build.gradle中,rootProject的排除配置只对rootProject有效,不会影响到其他subProject, 即sonarqube的排除配置只对其所属的工程有效。
注意,jacoco排除的文件并不会影响sonarqube的分析结果,尽管sonarqube使用了jacocoTestReport生成的xml文件进行分析,但是sonarqube分析的整个范围是由sonar.sources(默认值为${sourceSets.main.allSource.srcDirs})决定的,请参考https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/。简单说来,jacoco与sonar都有自己的“分析集”,jacocoTestReport排除的文件并没有影响到sonar的“分析集”大小。
代码覆盖信息是由test所生成的*.exec
文件决定的,jacocoTestReport可以直接利用*.exec
进行分析,而sonarqube需要利用jacocoTestReport基于*.exec
所生成的xml文件来进行分析,即它们所拥有的代码覆盖信息本质上是一样的,因此jacoco与sonarqube的文件排除,只会改变相应的“分析集”大小,不会互相影响相应的分析。