GitLabでJavadocやらJUnitやらJaCoCoやらの結果を表示する

GitLabで以下を試した時のメモ。
- GitLab PagesでJavadocで生成したhtmlを表示
- GitLabのMerge RequestからJUnitとJaCoCoの結果を表示

なお、これらを実施するにあたり、以下を実行するJobをGitLabのCI/CDパイプライン上に実装した。

- JUnit、JaCoCoの実行
- Javadocの生成
- 生成物のPagesへの引き渡し

今回ベースとしたサンプルコードは[こちら](https://github.com/junit-team/junit5-samples/tree/main/junit5-jupiter-starter-gradle)で、修正後のコードは[こちら](https://github.com/imurata/gitlab-pages-test-gradle)となる。


## サンプルコードの作成
[JUnitのサンプル](https://github.com/junit-team/junit5-samples/tree/main/junit5-jupiter-starter-gradle)を自環境に取り込み、Javadoc確認用コメント、JUnit、JaCoCo向けコードおよび`.gitlab-ci.yaml`を作り込んでいく。

このサンプルには最初からテストは実装されているため、JUnitの確認はすぐ出来るようになっている。
```java:src/test/java/com/example/project/CalculatorTests.java
	@Test
	@DisplayName("1 + 1 = 2")
	void addsTwoNumbers() {
		Calculator calculator = new Calculator();
		assertEquals(2, calculator.add(1, 1), "1 + 1 should equal 2");
	}
```

ただし、コメントがないのでJavadocの確認用にコメントを付与する。

```java:src/main/java/com/example/project/Calculator.java
package com.example.project;

/**
 * Calculatorクラスのコメント
 * 
 * @author hogehoge
 */
public class Calculator {

	/**
	 * addは引数の和を返します
	 * 
	 * @param a 第一引数
	 * @param b 第二引数
	 * @return 第一引数と第二引数の和
	 */
	public int add(int a, int b) {
		return a + b;
	}

}
```
JUnitは`build.gradle`内でtestで使うよう定義済みである。
```groovy:build.gradle
test {
	useJUnitPlatform()
	testLogging {
		events "passed", "skipped", "failed"
	}
}
```
そのため、この時点でJUnitとJavadocはそれぞれ利用可能になっているため、それぞれを動作確認する。
```
$ ./gradlew test
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Task :test

CalculatorTests > 1 + 1 = 2 PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[1] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[2] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[3] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[4] PASSED

BUILD SUCCESSFUL in 8s
3 actionable tasks: 3 executed
```
```
$ ./gradlew javadoc

BUILD SUCCESSFUL in 2s
2 actionable tasks: 1 executed, 1 up-to-date
```
また、build以下にドキュメントも生成されていることが分かる。
```bash
$ find build/ |grep  /index.html
build/docs/javadoc/index.html
build/reports/tests/test/index.html
```

次に、JaCoCoも実行できるようにする。JaCoCoは標準では動かないので、`build.gradle`に以下を追記する。
細かい設定をしたい人は[こちら](https://docs.gradle.org/current/userguide/jacoco_plugin.html)を参照するとよい。
```groovy:build.gradle
plugins {
  id 'jacoco'
}
jacocoTestReport {
    reports {
        xml.required = true
        csv.required = false
        html.required = false
    }
    dependsOn test
}
test {
    finalizedBy jacocoTestReport
}
```
動作確認する。
```
$ ./gradlew test

> Task :test

CalculatorTests > 1 + 1 = 2 PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[1] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[2] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[3] PASSED

CalculatorTests > add(int, int, int) > com.example.project.CalculatorTests.add(int, int, int)[4] PASSED

BUILD SUCCESSFUL in 4s
4 actionable tasks: 4 executed
```
レポートは`build/reports/jacoco/test/jacocoTestReport.xml`に出力される。


## `.gitlab-ci.yml`の作成
GitLab PagesとMerge Requestに表示させるために、以下のジョブを含む`.gitlab-ci.yml`を作成する。
- `make-javadoc`：Javadoc生成
- `unit-test`：JUnitとJaCoCoのレポート生成
- `convert-to-cobertura`：JaCoCoレポートをCobertura形式に変換
- `pages`：GitLab Pagesで表示

ポイントとしては以下となる。
- UnitTest、Coverageの結果をGitLabのArtifactに指定すると、Merge Requestで表示されるようになる([Test coverage visualization](https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html))。
- JaCoCoのレポートがGitLabで直接扱えないため、Cobertura形式に変換する（変換には[こちら](https://github.com/rix0rrr/cover2cover)を使う）

JaCoCoのレポートが直接使えない問題はGitLabの[こちらのIssue](https://gitlab.com/gitlab-org/gitlab/-/issues/227345)で議論されている。近い将来サポートされるようなことが書いているので、興味がある人はwatchしておくとよい。

それぞれのJobの依存関係は以下。
```mermaid
flowchart LR
    unit-test --> convert-to-cobertura
    make-javadoc --> pages
```


### Javadoc生成用Job
Javadoc生成のJobは以下となる。
```yaml
make-javadoc:
  stage: unit-test
  rules:
  - if: $CI_PIPELINE_SOURCE == "push"
  image: gradle:7.5.0-jdk17
  script:
      - |
        ./gradlew javadoc
        cp -r build/docs/javadoc/ public/
artifacts:
    paths:
    - public/
```
Pagesに生成したドキュメントをartifactsで渡す前にpublicディレクトリを作成している。\
これはGitLab Pagesがpublic以下にあるhtmlを見に行くため、こちらでpublicに整形している。
なお、public以外のディレクトリは[使えない模様](https://www.gitlab.jp/blog/2020/06/29/how-to-use-gitlab-pages/)。


### JUnitとJaCoCoのレポート生成用Job
JUnitとJaCoCoのレポート生成のJobは以下となる。
```yaml
unit-test:
  stage: unit-test
  rules:
  - if: $CI_PIPELINE_SOURCE == "push"
  image: gradle:7.5.0-jdk17
  allow_failure: true
  script:
      - |
        ./gradlew test
artifacts:
    reports:
      junit: build/test-results/**/TEST-*.xml
    paths:
      - build/reports/jacoco/test/jacocoTestReport.xml
```

`artifacts.reports.junit`でJUnitの結果を渡している。
なお、[GitLabのドキュメント](https://gitlab-docs.creationline.com/ee/ci/junit_test_reports.html#gradle)の書き方だとファイルを見つけてくれなかったので、オフィシャルのものから少しパスの表現を変えている。\
また、合わせてJaCoCoの結果もartifactsに渡しているが、これはGitLab向けではなく後続のJobに渡すためである。

### JaCoCoレポートをCobertura形式に変換するJob
JaCoCoレポートをCobertura形式に変換するJobは以下となる。
```yaml
  needs:
    - job: unit-test
      artifacts: true
  stage: unit-test
  image: haynes/jacoco2cobertura:1.0.7
  script:
    - python /opt/cover2cover.py build/reports/jacoco/test/jacocoTestReport.xml $CI_PROJECT_DIR/src/main/java/ > build/reports/coverage.xml
  artifacts:
    reports:
      coverage_report: 
        coverage_format: cobertura
        path: build/reports/coverage.xml
```
`needs`で前提Jobを定義し、`artifacts:true`を設定することでJaCoCoの結果を受け取れるようにしている。
コンテナイメージやスクリプトは[こちら](https://gitlab.com/haynes/jacoco2cobertura)を参考にした。


### GitLab Pagesで表示するJob
Pagesで表示するためのJobは以下となる。
```yaml
pages:
  needs:
    - job: make-javadoc
      artifacts: true
  rules:
  - if: $CI_PIPELINE_SOURCE == "push"
  image: busybox
  stage: pages
  script:
  - echo "The site will be deployed to $CI_PAGES_URL"
  artifacts:
    paths:
      - public/
```
やることは特になく、Javadoc作成したpublicディレクトリをそのまま渡している。また、こちらも`needs`でmake-javadocとの依存関係を記載した。

最終的な`.gitlab-ci.yml`は[こちら](https://github.com/imurata/gitlab-pages-test-gradle/blob/main/.gitlab-ci.yml)となる。

## 動作確認
適当なMerge Requestを作成し、以下の修正を入れてコミットした。

```diff:src/main/java/com/example/project/Calculator.java
	public int add(int a, int b) {
		return a + b;
	}
-
+	public int sub(int a, int b) {
+		return a - b;
+	}
  }
```
パイプラインの全てのJobが完了した後、それぞれどうなったか見ていく。

まず、該当するMerge requestのChangesを見ると、緑と赤の線が表示されるようになる。緑の線はカバー出来ており、赤の線はカバー出来ていない箇所となる。
![1663046391741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/14071964-1df3-6691-4043-1f2e73c7ee00.png)

今回新規に追加したメソッドに対応するテストは書かなかったため、カバーされていないことがこれで分かる。

次にJUnitの結果についても確認する。
先程実行したパイプラインを開くと、"Tests 5"というタブが増えていることが分かる。
![1663046643157.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/58027dda-bd09-f686-2f97-2c2900486a4b.png)
クリックすると、テストの件数やどのテストで失敗したかなどが分かる。
![1663046716891.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/6a9e5408-5d0d-f17c-a839-95fcd8664601.png)

最後にJavadocの内容をGitLab Pagesから確認する。\
GitLab PagesのURLは`https://<Group名>.pages.<gitlabのドメイン>/<Project名>`となっており、サイドバーのSettings->Pagesから辿ることが出来る。
また環境変数`CI_PAGES_URL`の中にも入っているため、パイプライン上で表示させれば、Jobの確認のついでに参照することもできる。

実際にアクセスした結果はこちら。先程書いたコメントが反映されていることが分かる。
![1663047060113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/9e54d698-3bac-a973-9d86-a0ba5f91e918.png)