テストを書こうと言っても、精神論ではなかなかその文化は育ちません。 これを行おうとすると、自分の記述したテストがどのようにプロジェクトやチームに貢献しているのかを可視化する必要があります。
今日のゴールは2つあり、1つはMerge Requestのファイルタブ上でテストのカバー範囲を可視化することです。
そしてもう1つは、Merge Request上でカバレッジが何%なのかを表示することです。
JaCoCoの組み込み
Javaにおいてカバレッジを解析してくれるライブラリにJaCoCoがあります。
JaCoCoについてはMaven Plug-inが存在しています。
このため、pom.xml
で設定すれば、カバレッジのレポートを取ることが可能になります。
以下がjacoco-maven-plugin
を設定するpom.xml
です。
diff --git a/app/movie-uploader/pom.xml b/app/movie-uploader/pom.xml index 07b9927..82e2305 100644 --- a/app/movie-uploader/pom.xml +++ b/app/movie-uploader/pom.xml @@ -180,6 +180,26 @@ </dependency> </dependencies> </plugin> + + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.7</version> + <executions> + <execution> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build>
mvn test
を実行すればカバレッジレポートが出力されるようになります。
$ mvn test (略) [INFO] --- jacoco-maven-plugin:0.8.7:report (report) @ movieuploader --- [INFO] Loading execution data file /Users/kiririmode/src/github.com/kiririmode/hobby/app/movie-uploader/target/jacoco.exec [INFO] Analyzed bundle 'movieuploader' with 6 classes
テストカバレッジの可視化
GitLabのTest Coverage Visualization機能
GitLabには、Merge Requestで表示されるソース行をテストがカバーしたのか否かを可視化してくれる機能があります。
この機能は、.gitlab-ci.yml
にてCovertura形式のファイルをartifacts:reports:coverage_report
で指定することにより有効になります。
JaCoCoからCovertura形式への変更
しかし冒頭に記載した通り、ぼくが使っているのはJaCoCoです。従って、JaCoCoからCovertura形式のカバレッジレポートへと変換しなければなりません。
JaCoCo形式からCovertura形式への変換機能を有するコンテナはGitLabやDockerHubでホストされているので、これを利用すれば良いでしょう。
Dockerイメージ上に含まれているのはcover2cover.py
で、引数は以下のようでした。
- JaCoCoのレポートへのパス
- Javaのソースルートへのパス
これをgitlab-ci.yml
に落とすと以下のcoverage
ジョブのようになります。
unittest: stage: test image: maven:3.8.4-openjdk-17-slim (略) script: - mvn ${MAVEN_CLI_OPTS} -f app/movie-uploader/pom.xml test (略) artifacts: paths: - app/movie-uploader/target/site/jacoco/jacoco.xml coverage: stage: visualize image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.8 variables: jacocoreport: app/movie-uploader/target/site/jacoco/jacoco.xml script: - python /opt/cover2cover.py ${jacocoreport} ${CI_PROJECT_DIR}/app/movie-uploader/src/main/java/ > cobertura.xml rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' changes: - "app/**/*" artifacts: reports: coverage_report: coverage_format: cobertura path: cobertura.xml
これを実行すると、きちんとテストのカバー範囲が表示されるようになりました。
Merge Requestへのカバレッジの表示
さらにMerge Request上で、現在のテストカバレッジが何%なのかという表示も行ってみます。
JaCoCoのXML形式のレポートには、末尾にC1やC2カバレッジの元となる数値が記録されています。
$ xmllint --format target/site/jacoco/jacoco.xml | tail -8 </package> <counter type="INSTRUCTION" missed="266" covered="123"/> <counter type="BRANCH" missed="10" covered="4"/> <counter type="LINE" missed="71" covered="25"/> <counter type="COMPLEXITY" missed="23" covered="9"/> <counter type="METHOD" missed="18" covered="7"/> <counter type="CLASS" missed="4" covered="2"/> </report>
これをxmllint
で取り出せば、容易にC0やC1カバレッジが計算できるでしょう。ここでは、XPathで値を抽出し、C1カバレッジを計算することにしました。
$ jacocoreport=target/site/jacoco/jacoco.xml $ covered=$(xmllint --xpath 'string(/report/counter[@type="BRANCH"]/@covered)' ${jacocoreport}) $ missed=$(xmllint --xpath 'string(/report/counter[@type="BRANCH"]/@missed)' ${jacocoreport}) $ coverage=$(awk -vmissed=$missed -vcovered=$covered 'BEGIN{ printf("%.1f\n", covered/(covered+missed)*100 ) }') $ echo "C1 coverage=${coverage}%" C1 coverage=28.6%
これを.gitlab-ci.yml
に組み込めば良いです。
GitLabの場合、カバレッジはジョブの標準出力に出力し、それをキャプチャする仕組みがありました。
最近はこのキャプチャ範囲を.gitlab-ci.yml
で書けるようになったようです。
coverage: stage: visualize image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.8 variables: jacocoreport: app/movie-uploader/target/site/jacoco/jacoco.xml before_script: # for xmllint - apk --no-cache add libxml2-utils script: - python /opt/cover2cover.py ${jacocoreport} ${CI_PROJECT_DIR}/app/movie-uploader/src/main/java/ > cobertura.xml - covered=$(xmllint --xpath 'string(/report/counter[@type="BRANCH"]/@covered)' ${jacocoreport}) - missed=$(xmllint --xpath 'string(/report/counter[@type="BRANCH"]/@missed)' ${jacocoreport}) - coverage=$(awk -vmissed=$missed -vcovered=$covered 'BEGIN{ printf("%.1f\n", covered/(covered+missed)*100 ) }') - echo "Test Coverage=${coverage}%" rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' changes: - "app/**/*" coverage: '/Test Coverage=\d+\.\d+/' artifacts: reports: coverage_report: coverage_format: cobertura path: cobertura.xml
結果がこちら。