Jacoco与SonarQube在Gradle多工程中的应用

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的文件排除,只会改变相应的“分析集”大小,不会互相影响相应的分析。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339