CodeBuildのテストレポート機能でコードカバレッジレポートがサポートされたから、また使ってケチつけてみた

# TL; DR

結論から言えば、オススメです。
下記のような画面を見て、問題なさそうだと思った方は、是非とも使ってみてください。

<img width="1220" alt="レポートグループの画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/1f64eb98-f4eb-4d41-ce86-3321bf3bcd74.png">

現在では、未実行ラインを表示／確認ができません。
しかし、実装される未来は近い気がします。

AWS様ありがとうございます、一生ついていきます！
ですが、あともう一声、お願いします！！！



# はじめに

2020年5月22日になり、テストレポート機能が一般公開されました。

[AWS CodeBuild テストレポートの一般公開が開始](https://aws.amazon.com/jp/about-aws/whats-new/2020/05/aws-codebuild-test-reporting-is-now-generally-available/)

それから早2ヶ月後の7月30日、ついに欲しかったコードカバレッジレポートのサポートがされました。

[AWS CodeBuild がコードカバレッジレポートをサポート](https://aws.amazon.com/jp/about-aws/whats-new/2020/07/aws-codebuild-supports-code-coverage-reporting/)

この機能がどのようなものなのか、実際に動かして確かめてみたくありませんか？
ということで、実際に試してみます。

なお、本記事は下記の続きとして記載しています。
よろしければ、下記もご覧ください。

[CodeBuildのテストレポート機能が一般公開されたから使ってケチつけてみた - Qiita](https://qiita.com/Morichan/items/d747f429067c082bc982)



# 事前準備

コードカバレッジレポートってなんやねん、という方は、ざっと流し読みしてみてください。


## コードカバレッジレポート

ここでいうコードカバレッジレポートは、テスト実行時のソースコードカバレッジ（網羅率）を集計したファイルのことです。

[カバレッジ(網羅率)分析とは | ソフトウェアの検証の種類 | テクマトリックス株式会社](https://www.techmatrix.co.jp/t/quality/coverage.html)

JaCoCoが有名ですが、それ以外にも多くのフォーマットがあるみたいです。

CodeBuildのレポート機能では、上記コードカバレッジレポートファイルを組込むことで、カバレッジを可視化できます。
似たようなサービスとして、 [Codecov](https://codecov.io/) や [Coveralls](https://coveralls.io/) が挙げられますね。



# 実際に試してみた

CodeBuildで、テストレポート機能におけるコードカバレッジレポートの表示動作確認をしてみましょう。

なお、CodeCommitとCodeBuildのプロジェクトの新規作成については、 [以前の記事で記載](https://qiita.com/Morichan/items/d747f429067c082bc982#codecommitとcodebuildのプロジェクトを新規作成する) しているため、省略します。


## CodeCommitにソースコードをプッシュする

CodeCommitに、CodeBuildでビルドするためのソースコードをプッシュします。

動作確認ができればなんでも良いので、好きな言語で試してみましょう。
つまり僕は、前回同様Perlで試してみるわけです。

ディレクトリ構造は、下記の通りです。

```bash:CodeCommitリポジトリのディレクトリ構造
test-reports-sample/
 |- buildspec.yml # CodeBuildの実行ファイル
 |- lib/
 |   |- sample.pm # テスト対象コード（モジュール）
 |
 |- t/
     |- sample.t # テストコード
```

まず、テスト対象コードです。

```pl:lib/sample.pm
use strict;
use warnings;
use utf8;

package Sample;

sub get_one {
    my ($self, $is_true) = @_;

    print "True¥n" if $is_true;

    return 1;
}

sub get_two {
    return 2;
}

1;
```

このうち、ブランチカバレッジの表示用に書いている箇所は `print "True¥n" if $is_true;` の部分です。
`$is_true` は引数に応じて変化します。
引数が真の場合 （Perlなので `1` などを引数に設定した場合）、 `print "True¥n"` が実行されます。

次に、テストコードです。

```pl:t/sample.t
use strict;
use warnings;
use utf8;

use Test::More;

use lib 'lib';
use sample;

subtest 'サンプルテスト' => sub {
    my $test = "pass";
    ok $test;
};

subtest 'サンプルテスト2' => sub {
    my $expected = 1;

    my $actual = Sample->get_one;

    is $actual, $expected;
};

done_testing;
```

`サンプルテスト` については、以前のコードと同じため、説明は省略します。

`use lib 'lib';` にて `lib/` ディレクトリにパスを通し、 `use sample` で `lib/sample.pm` モジュールをインポートします。
また、 `サンプルテスト2` における `Sample->get_one` により、 `lib/sample.pm` モジュールにおける `get_one` 関数を実行します。

下記2点がポイントです。

- `get_two` 関数を呼出していない
- `if $is_true` は真になり得ず、ブランチカバレッジは100%にならない

CodeBuildでは、buildspec.ymlファイルにより動作を定義できるため、こちらも作ります。

```yaml:buildspec.yml
version: 0.2

phases:
  install:
    commands:
      - curl -L https://cpanmin.us/ -o cpanm && mv cpanm /usr/bin/cpanm && chmod +x /usr/bin/cpanm
      - curl -fsSL --compressed https://git.io/cpm > cpm && mv cpm /usr/bin/cpm && chmod +x /usr/bin/cpm
  build:
    commands:
      - cpm install -g TAP::Harness::JUnit
      - cpm install -g Devel::Cover::Report::Clover
      # - prove -lv -j5 --harness TAP::Harness::JUnit sample.t # テストレポートだけを出力する場合のコマンド
      - HARNESS_PERL_SWITCHES="-MDevel::Cover=+ignore,^t" prove -lvr -j5 --harness TAP::Harness::JUnit t
      - cover -report clover

reports:
  sample-test-report:
    files:
      - junit_output.xml
  sample-coverage-clover-report:
    file-format: CloverXml
    files:
      - cover_db/clover.xml
```

`Devel::Cover::Report::Clover` モジュールをインストールすることで、Clover形式のXMLを出力できます。

`HARNESS_PERL_SWITCHES="-MDevel::Cover=+ignore,^t" prove -lvr -j5 --harness TAP::Harness::JUnit t` コマンドにて、 `junit_output.xml` テストレポートファイルを生成し、同時にテストカバレッジを計測するため `cover_db` ディレクトリを生成します。
その後 `cover -report clover` コマンドにて、Clover形式のXMLである `cover_db/clover.xml` ファイルを生成します。

上記 `cover_db/clover.xml` ファイルを、 `reports` における `sample-coverage-clover-report` にて、 `CloverXml` フォーマットを指定して設定します。

<details>
<summary>以降、前回との差分とPerl固有の内容についての詳細です。</summary>
<p>前回のCodeBuildイメージは Amazon Linux だったためか、なぜか依存モジュールをインストールしてくれなくて大変な思いをしました。
今回は Ubuntu イメージにしたことで、 `expat-devel` のDebian版ライブラリ `libexpat1-dev` をインストールする必要がなくなり、必要なPerlモジュールだけをインストールするだけで依存モジュールのインストールもしてくれました。
</p>
<p>
また、 `Devel::Cover` モジュールの `cover` コマンドには、JSON形式のコードカバレッジレポートファイルを生成してくれます。
しかし、CodeBuildのテストレポート機能で対応しているSimpleCov JSON形式とは異なるため、正常に読み込んでくれません。
`Devel::Cover::Report::Clover` 以外であっても、CodeBuildのテストレポート機能が対応している形式で出力してあげれば、正常に読み込んでくれると思います。
</p>
</details>

- CodeBuildに関するもの
    - [Code coverage reports - AWS CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/code-coverage-report.html)
    - [Build specification reference for CodeBuild - AWS CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#reports-buildspec-file)
- Perlに関するもの
    - [cover - report coverage statistics - metacpan.org](https://metacpan.org/pod/distribution/Devel-Cover/bin/cover)
    - [Devel::Cover - Code coverage metrics for Perl - metacpan.org](https://metacpan.org/pod/Devel::Cover)
    - [Devel::Cover::Report::Clover - Backend for Clover reporting of coverage statistics - metacpan.org](https://metacpan.org/pod/Devel::Cover::Report::Clover)
    - [第49回　CPANモジュールの品質を支えるCI技術（3）：Perl Hackers Hub｜gihyo.jp … 技術評論社](https://gihyo.jp/dev/serial/01/perl-hackers-hub/004903)
    - [lib - モジュールの検索パスを追加 - Perlゼミ](https://tutorial.perlzemi.com/blog/20100525127859.html)
    - [perl - How to install XML::Parser without expat-devel? - Stack Overflow](https://stackoverflow.com/questions/9693031/how-to-install-xmlparser-without-expat-devel)


## CodeBuildで自動テストする

上記3ファイルを入れたリポジトリで、CodeCommit経由でCodeBuildを走らせます。
CodeBuildによるビルドは、手動で構いません。


## CodeBuildでビルド通過後にテストレポートを確認する

CodeBuildでビルドが完了したら、テストレポートを確認しましょう。

CodeBuildにtest-reports-sampleビルドプロジェクトを作成し、ビルド結果のレポートタブを開きます。

<img width="1219" alt="test-reports-sampleビルドプロジェクトにおけるレポートタブを開いた画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/de045a55-7c88-9f8c-10c9-c06ebd126193.png">

以前までの画面とは異なり、「タイプ」という項目が追加されています。
また、タイプの中身として「テスト」と「コードカバレッジ」の2種類が存在することがわかります。

では、コードカバレッジである `test-report-sample-sample-coverage-clover-report` グループのレポートを見てみましょう。

<img width="1216" alt="test-report-sample-sample-coverage-clover-reportグループのコードカバレッジレポート結果画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/c251a77d-35ee-ab58-8fd9-fa2faf7d8640.png">

カバレッジ測定結果として、ラインカバレッジとブランチカバレッジの円グラフがデカデカと載っていることがわかります。
また、 `lib/sample.pm` ファイルを計測していることが、一覧で表示されていることがわかります。

レポートグループ画面は、下記の通り（再掲）です。
カバレッジの履歴が一眼でわかります。
~~平均の数値はよくわかりませんが、~~ こっちは便利そうですね。

<img width="1220" alt="レポートグループの画面（再掲）" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/1f64eb98-f4eb-4d41-ce86-3321bf3bcd74.png">


ただ、ここでわかりづらいのですが、ラインカバレッジとブランチカバレッジは独立している、という点です。
それぞれについて、説明します。


## ラインカバレッジとブランチカバレッジの解説

**以降は、Perlにおける `Devel::Cover` モジュール固有の問題である可能性もあるので、話半分で聞いてください。**

ラインカバレッジの項目では、1行だけ実行されなかったことがわかります。
ここでいう未実行の1行とは、 `get_two` 関数における `return 2;` の行のことを指します。

その証拠に、 `get_two` 関数を呼出すテストコードを追記した場合、下記のような画面になります。

```pl:サンプルテスト3を追加したテストコード（抜粋）
subtest 'サンプルテスト3' => sub {
    my $expected = 2;

    my $actual = Sample->get_two;

    is $actual, $expected;
};
```

~~ラインカバレッジ0って何......？~~

<img width="1219" alt="ラインカバレッジだけが100%のコードカバレッジレポート結果画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/87adac6b-c3db-7a14-d03f-bbf5dc5ec6e5.png">

つまり、 `print "True¥n" if $is_true;` の行における `print "True¥n"` は実行されていないにも関わらず、行単位では実行しているためにラインカバレッジは100%になった、というわけです[^ラインカバレッジ]。

[^ラインカバレッジ]: ラインカバレッジの定義としては、別に間違っていません。これに気をつけるために、ブランチカバレッジという物があるわけです。

一方、ブランチカバレッジは2つしかありませんが、そのうち1つは `print "True¥n" if $is_true;` の `$is_true` が 真である場合の実行分岐を指しています。

その証拠に、 `$is_true` が真になる `get_one` 関数を呼出すテストコードを追記した場合、下記のような画面になります。
サンプルテスト3は、スキップして実行します。

```pl:サンプルテスト4を追加したテストコード（抜粋）
subtest 'サンプルテスト3' => sub {
    plan(skip_all => 'ignore test'); # サンプルテスト3はスキップする

    my $expected = 2;

    my $actual = Sample->get_two;

    is $actual, $expected;
};

subtest 'サンプルテスト4' => sub {
    my $expected = 1;

    my $actual = Sample->get_one(1);

    is $actual, $expected;
};
```

~~ブランチカバレッジ0って何......？~~

<img width="1218" alt="ブランチカバレッジだけが100%のコードカバレッジレポート結果画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/49a12c86-a15d-03ee-4dad-3e885dc94623.png">

ここで注意すべき点としては、**ブランチカバレッジはラインカバレッジの上位互換ではない**、という点です。
$ C0 \leqq C1 $ という不等式が成立しない点に注意してください。

ちなみに、全てのカバレッジを100%にすると、下記のような表示画面になります。

<img width="1218" alt="ラインカバレッジおよびブランチカバレッジが100%のコードカバレッジレポート結果画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/216416/efc9221c-cb49-e1e1-72b4-6091ae4d1714.png">



# CodeBuildテストレポート機能の利点／欠点

機能自体は理解できたので、CodeBuildテストレポート機能におけるコードカバレッジレポートの利点と欠点を、それぞれ書いていきます。


## 利点

前回と被る面もありますが、利点を挙げていきます。

### グラフィカルなUIでテスト結果が確認できる

以前までは、CodeBuildのテスト結果は全てCloudWatch LogsのCUIライクな表示でしか確認できませんでした。
テストレポートだけでなくコードカバレッジレポートもグラフィカルに表示できることで、テストレポートの必要最低限の機能は満たせたのではないかと考えます。

### 折れ線グラフがわかりやすい

僕の書いた記事を、AWSの中の人が読んでくださったのか定かではありませんが、折れ線グラフが非常にわかりやすいです。
折れ線グラフであれば直近のカバレッジが見えますし、どれだけカバレッジを増やしているかも一目瞭然です。

https://qiita.com/Morichan/items/d747f429067c082bc982#平均テスト通過率平均実行数の意味がわからない

### 別サービスを契約しなくて良い

現在の機能だけでも、CodecovやCoverallsなどの別サービスを契約する必要性は下がります。
CodeCommitを使う際はプライベートリポジトリの場合が多いため、プライベートリポジトリの場合は有料契約になってしまう上記サービスを使わなくても良い点は、利点としてかなり大きいと思います。


## 欠点

いくつか気になる点はあるので、~~（また、タイトルでもケチつけると書いちゃったので、）~~粗探しをします。
ただ、前回よりはずいぶんよくなったと思います。

### 平均ラインカバレッジ率／平均ブランチカバレッジ率の意味がわからない

直前以外の平均ラインカバレッジ／平均ブランチカバレッジも、あまり使わない気がします。
大事なのは、直前のカバレッジだと思うのですが、なぜ平均値を出しているのか、よくわかりません。
しかも、1度でもテストで失敗してしまうと（理論上）100%には二度と戻らないのも、悲しいです。
単純に、最新カバレッジを表示するのではダメなのでしょうか？

あと、ちょっとしたことですが、カバレッジ率を日本語に訳すと**網羅率率**ですか？？？

### 平均ラインカバレッジ／平均ブランチカバレッジが100%の時の表示が気になる

せっかく頑張って100%にしたのに、 `0` は悲しいです。
ここは `100%` にしてほしいなーと思います。

### レポートグループタイプが固定

一度レポートグループを作成してしまうと、作成したレポートグループのタイプを変更できません。

レポートグループタイプおよびレポートタイプは、 `TEST` と `CODE_COVERAGE` の2種類があります。
レポートタイプは、 `buildspec.yml` で指定した `file-format` で、下記に依存して自動的に設定されます。

> The following test report file formats are supported:
>
> - CucumberJson
> - JunitXml
> - NunitXml
> - TestNGXml
> - VisualStudioTrx
>
> The following code coverage report formats are supported:
>
> - JaCoCoXml
> - SimpleCov
> - CloverXml
> - CoberturaXml
>
> [Build specification reference for CodeBuild - AWS CodeBuild](https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#reports-buildspec-file)

前者が `TEST` 、後者が `CODE_COVERAGE` として、内部処理で分けているみたいです。

また、レポートグループタイプは、最初に `buildspec.yml` でレポートを自動生成する際に設定した `file-format` の値に依存したタイプが、これまた自動的に設定されます。

レポートグループタイプは（AWSマネジメントコンソール上でも）変更できないため、レポートグループを削除する or レポートグループ名を変更する、いずれかをしない限り、次のようなエラーが発生し、CodeBuildがエラー終了します。

> CLIENT_ERROR: Error in UPLOAD_ARTIFACTS phase:
> [CoverageReports: [error creating report: InvalidInputException: Report group type doesn't match expected report type, report group type is TEST, but report type is CODE_COVERAGE]]
>
> 翻訳結果:
> CLIENT_ERROR：UPLOAD_ARTIFACTSフェーズのエラー：
> [CoverageReports: [レポートの作成エラー: InvalidInputException: レポートグループタイプが予期されるレポートタイプと一致しません。レポートグループタイプはTESTですが、レポートタイプはCODE_COVERAGEです]]

つまり、最初に `file-format` を間違えて設定してしまうと、以降はCodeBuildのフェーズエラーを見ない限りエラー内容に気付けません。
最初に使おうと試行錯誤している利用者には、思わぬ落とし穴になりそうです。

すみませんが、この問題の解決策はすぐには思いつきそうにありません。
今後のアップデートに期待します。



# おわりに

タイトルでは貶しているように見えたらごめんなさい。
中身としては、結構ベタ褒めで書いたつもりです。

それだけ、僕はこの機能を求めていました。
AWSの中の人、本当にお疲れ様です！

今後もアップデートがあると信じて、期待しましょう！！！