JaCoCoのカバレッジをコンソールに出力する

GitLab CIを使っている場合、 [Coveralls](https://coveralls.io/) などの外部サービスやJenkinsと連携させなくても、コンソール出力からテストカバレッジをパースしてコミットとひも付ける機能（ [Test coverage parsing](https://docs.gitlab.com/ee/user/project/pipelines/settings.html#test-coverage-parsing) ）があって便利です。しかし、肝心の [Gradle JaCoCoプラグイン](https://docs.gradle.org/current/userguide/jacoco_plugin.html) にはカバレッジをコンソール出力する機能がありません。

ここでは、JaCoCoで出力したレポートXMLファイルを自前でパースすることで、標準出力にテストカバレッジを出力する方法を紹介します。なお、この方法は [SpringFox](https://github.com/springfox/springfox/blob/v1.0.2/gradle/coverage.gradle) のビルドファイルを参考にさせてもらいました。

## 動作確認バージョン

* macOS 10.14.2
* OpenJDK 11
* Gradle 4.10
* JaCoCo 0.8.1

## ビルドファイル

こんな感じ。Groovyチョットデキないのでいい加減です。

```gradle
plugins {
    id 'jacoco'
}

jacocoTestReport {
    reports {
        xml.enabled = true
        html.enabled = true
    }
    doLast {
        def report = file("${jacoco.reportsDir}/test/jacocoTestReport.xml")
        printCoverage(report)
    }
}

test.finalizedBy jacocoTestReport

def printCoverage(File xml) {
    if (!xml.exists()) return

    def parser = new XmlParser()
    parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
    parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false)
    def results = parser.parse(xml)

    def percentage = {
        def covered = it.'@covered' as Double
        def missed = it.'@missed' as Double
        ((covered / (covered + missed)) * 100).round(2)
    }

    def counters = results.counter
    def metrics = [
            'instruction': percentage(counters.find { it.'@type'.equals('INSTRUCTION') }),
            'branch'     : percentage(counters.find { it.'@type'.equals('BRANCH') }),
            'line'       : percentage(counters.find { it.'@type'.equals('LINE') }),
            'complexity' : percentage(counters.find { it.'@type'.equals('COMPLEXITY') }),
            'method'     : percentage(counters.find { it.'@type'.equals('METHOD') }),
            'class'      : percentage(counters.find { it.'@type'.equals('CLASS') })
    ]

    logger.quiet("----- Code Coverage ----------")
    metrics.each { key, value -> logger.quiet(sprintf(" - %-11s: %6.2f%%", key, value)) }
    logger.quiet("------------------------------")
}
```

## 出力イメージ

テスト実行するとカバレッジがコンソールに出力されることが分かります。

```shell
$ ./gradlew test
...
> Task :jacocoTestReport
----- Code Coverage ----------
 - instruction:  27.32%
 - branch     :   3.50%
 - line       :  91.25%
 - complexity :  34.05%
 - method     :  72.94%
 - class      : 100.00%
------------------------------
...
```

## 補足

JaCoCoのレポートXMLは次のような形式になっているので、このXMLのトップレベルの `counter` 要素を探して、`missed` および `covered` の値からカバレッジを計算しています。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE report PUBLIC "-//JACOCO//DTD Report 1.0//EN" "report.dtd">
<report name="example">
    ...
    <counter type="INSTRUCTION" missed="931" covered="350" />
    <counter type="BRANCH" missed="193" covered="7" />
    <counter type="LINE" missed="7" covered="73" />
    <counter type="COMPLEXITY" missed="122" covered="63" />
    <counter type="METHOD" missed="23" covered="62" />
    <counter type="CLASS" missed="0" covered="9" />
</report>
```

## まとめ

GitLab CIのカバレッジ機能では他サービスのようにカバレッジの時間推移を追跡するような高度な機能はありませんが、手っ取り早くコミットにカバレッジをひも付けて表示させることができるので、最初のステップとしてはシンプルでよいです。

かなりニッチな気はしますが、JaCoCoでカバレッジをコンソール出力したいという人がいたら試してみてください。