【Flutter】テストカバレッジを効率よく出力する環境を整える

## はじめに

実務においてテストカバレッジをHTML形式で出力してスクリーンショットを撮り、プルリクエストに添付することはよくあることだと思います。
すでに環境が整っていた中で何気なく行っていたのですが、プライベートの環境でも構築してみたいと思い取り組んでみたところ簡単に導入できました。
今回はその導入手順を紹介したいと思います。

なお最初に通常の手順でテストカバレッジを出力する方法を紹介し、次に効率化した方法を紹介します。

### 記事の対象者

- テストカバレッジを出力してみたい方
- カバレッジを効率よく出力したい方

### 記事を執筆時点での筆者の環境

- macOS 14.3.1
- Xcode 15.2
- Swift 5.9
- iPhone11 pro ⇒ iOS 17.2.1
- Flutter 3.19.0
- Dart 3.3.0
- Pixel 7a ⇒ Android

## 1. テストカバレッジを出力する手順

以下は前提条件です。

1. IDEはVSCodeである
2. プロジェクトを開いている
3. テストコードが書いてある
4. HomeBrewがインストールされている

### 1-1. lcovのインストール

カバレッジデータを収集し、それを見やすいHTMLレポートに変換することができるツールをHomeBrew経由でインストールします。

以下のコマンドをターミナルで実行します。

```zsh
brew install lcov
```

### 1-2. テストを実行

カバレッジレポートを出力するオプションをつけて以下のコマンドをターミナルで実行します。

```zsh
flutter test --coverage
```

### 1-3. `lcov`ツールでカバレッジレポートから特定のファイルを除去

Flutterでの開発の場合、riverpodやfreezedなど自動生成系のパッケージはよく使っていると思います。
自動生成ファイルはテストカバレッジに含めたくないので、自動生成ファイルを除いた新しいカバレッジレポート`new_lcov.info`を以下のコマンドをターミナルで実行して生成します。

念の為、自動生成ファイルがなかった場合に備えて`--ignore-errors unused` オプションを追加することで、パターンに一致するファイルがない場合でもエラーを無視するように設定します。

```zsh
lcov --remove coverage/lcov.info 'lib/**.g.dart' -o coverage/new_lcov.info --ignore-errors unused
```

:::note info
プロジェクトで自動生成系を使っていなければここは実行しなくても良いです。
:::

### 1-4. `genhtml`ツールでHTML形式に変換

locvに含まれる`genhtml`ツールを使って以下のコマンドをターミナルで実行します。
生成されたカバレッジレポート`new_lcov.info`をHTML形式に変換し、`coverage/html`フォルダに出力します。

```zsh
genhtml coverage/new_lcov.info -o coverage/html
```

ここまでくると、以下の画像の内容でファイル達が出来上がっているはずです。
![スクリーンショット 2024-05-18 11.21.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/d6d95ebc-6b3a-d078-eb47-19e3b295488f.png)


### 1-5. カバレッジレポートをブラウザで表示

以下のコマンドを実行すると生成されたHTML形式のカバレッジレポートをデフォルトのブラウザで開きます。

```zsh
open coverage/html/index.html
```

以下のように表示されます。
テストは一つしか書いてないので、悲惨なカバレッジです😨

![スクリーンショット 2024-05-18 11.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/aa346f89-8474-184b-fb52-5d452f57a66e.png)

## 2. derryを使って効率よく出力する

上記のコマンドを毎回打つのは正直面倒ですよね。
そこで、derryというパッケージを使ってプロジェクトにスクリプトを作って一連の処理を一つのコマンで実行できるようにします。

**スクリプト**とは、コンピュータに一連の命令を実行させるための処理で、手作業で行う複数のステップを自動化できます。

https://pub.dev/packages/derry

### 2-1. derryをPCにインストール

derryをシステム全体で利用可能なコマンドラインツールとしてインストールします。

```zsh
dart pub global activate derry 
```

### 2-2. パスを通す

上記コマンドでパスが通るはずですが、通っていない場合も考えて確認します。
まずはターミナルのホームディレクトリで以下を実行して、`.zshrc`ファイルまたは`.bashrc`を開きます。

```bash
open .zshrc
```

または

```zsh
open .bashrc
```

そして以下の記述がなければ追記します。

```
export PATH="$PATH":"$HOME/.pub-cache/bin"
```

### 2-3. プロジェクトにderryをインストール

以下コマンドをターミナルにてプロジェクトのディレクトリで実行します。

```zsh
flutter pub add derry 
```

ファイルを保存して閉じます。

### 2-4. スクリプトを書く

pubspec.yamlに使いたいスクリプトを書きます。
コロン`：` の左辺にコマンド名、右側に実行するコマンドを書いていきます。
今回はコマンド名を`cov`にしましたが、ここはみなさんの使いやすいように変更してみてください。

コマンドの部分は１番目のコマンドを書いたら、半角スペース `&&`  半角スペースで続きの２番目のコマンド…
というように書いていきます。
今回でいくと次のような順番で実行するスクリプトになります。

1. テストを実行
2. `lcov`ツールでカバレッジレポートから特定のファイルを除去
3. `genhtml`ツールでHTML形式に変換
4. カバレッジレポートをブラウザで表示

上記をまとめると以下のようになります。

```pubspec.yaml
scripts:
  cov: flutter test --coverage && lcov --remove coverage/lcov.info 'lib/**.g.dart' -o coverage/new_lcov.info --ignore-errors unused && genhtml coverage/new_lcov.info -o coverage/html && open coverage/html/index.html
```
<details><summary>pubspec.yamlの全文はこちら</summary>

```pubspec.yaml
name: components_sample
description: "A new Flutter project."
publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=3.2.5 <4.0.0'

dependencies:
  flutter:
    sdk: flutter
  flutter_hooks:
  cupertino_icons: ^1.0.6
  gap:
  derry: ^1.5.0

dev_dependencies:
  flutter_test:
    sdk: flutter
  custom_lint:
  flutter_lints: ^3.0.0
  very_good_analysis: ^5.1.0

flutter:
  uses-material-design: true

# 🤚 ここにスクリプトを記入する 🖐️
scripts:
  cov: flutter test --coverage && lcov --remove coverage/lcov.info 'lib/**.g.dart' -o coverage/new_lcov.info --ignore-errors unused && genhtml coverage/new_lcov.info -o coverage/html && open coverage/html/index.html
```

</details>



::: note info
スクリプトの定義を別のyamlファイルに記述することもできます。
詳しくは公式の [API Documentation](https://pub.dev/packages/derry#api-documentation) をご覧ください
:::

### 2-5. コマンドを実行してみる

ターミナルで以下を実行してみます。

```zsh
derry cov
```

すると以下のような挙動でテスト実行からブラウザ展開まで行ってくれます。

![derry_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058346/8416868c-9856-46b6-e466-26b5d2daee7d.gif)


### 2-6. 余談：スニペットとしても使える

よく使うコマンドのスニペットとしても使えます。
以下で紹介している**br** と **gc**はともに１つのコマンドですが、ショートカットコマンドとして登録しています。

```pubspec.yaml
scripts:
  # build_runnerを実行して、同じファイル名があれば古いものを削除する
  br: flutter pub run build_runner build --delete-conflicting-outputs
  #  Gradle デーモンプロセスを終了する => ビルドエラーの解消
  gc: pkill -f '.*GradleDaemon.*'
  # テストを実行しカバレッジをHTMLで出力してブラウザで開くスクリプト
  cov: flutter test --coverage && lcov --remove coverage/lcov.info 'lib/**.g.dart' -o coverage/new_lcov.info --ignore-errors unused && genhtml coverage/new_lcov.info -o coverage/html && open coverage/html/index.html
```

## 終わりに

この記事では、テストカバレッジを出力する方法と、それを効率化するためのツールと手段について説明しました。
これらの手順とツールを活用することで、テストカバレッジの確認と管理が簡単になります。

この記事が誰かのお役に立てれば幸いです。

## 参考記事

https://qiita.com/takuro0987/items/17b8fc518340081410ff

https://zenn.dev/k9i/articles/c54446a72f1f46