【Flutter】GitHubActionsでテストと静的解析を自動化する

FlutterプロジェクトでGitHub Actionsを使って静的解析＆自動テストを行ってみましたので記事にします。
特定のブランチにプルリクしたときに静的解析が通るかどうかを調べて、テストを自動で行ってくれるようになります。
今回作業したプロジェクトは[こちら](https://github.com/tokku5552/TODOAppSample-Flutter/tree/v1.3.1)です。

# 静的解析を導入
Flutterの静的解析は色々とパッケージが用意されています。
こちらの記事に非常にわかりやすく書かれています。
[Dart/Flutter の静的解析強化のススメ](https://medium.com/flutter-jp/analysis-b8dbb19d3978)
[【Dart/Flutter】静的解析の強化と注意](https://kabochapo.hateblo.jp/entry/2019/03/18/230246)

基本的には、静的解析のためのパッケージ(lintやpedanticなど)を導入して、プロジェクト直下にコーディングルールを指定するための`analysis_options.yaml`を配置すると、そのルールに従ってIDEが警告を出してくれたり、コンパイル時に確認してくれるようになります。

私の場合は純粋に[pedantic](https://pub.dev/packages/pedantic)を導入するにとどめました。
以下のパッケージを入れて、

```yaml:pubspec.yaml
dev_dependencies:
  pedantic: ^1.9.0
```

[pedanticの公式GitHubリポジトリのlib/](https://github.com/google/pedantic/tree/master/lib)に`analysis_options.yaml`の例がありますので、これ真似して自分のプロジェクトの直下にコピーしておいても良いですし、
pedanticの導入バージョンの最新のルールで良ければ、公式のやり方に従って、プロジェクト直下に以下のファイルを配置すればOKです。

```yaml:analysis_options.yaml
include: package:pedantic/analysis_options.yaml
```

Android Studioの画面最下部の**Dart Analysis**タブを開くと、エラーや警告が一覧となって確認できます。
![analysis.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/cf93ed2e-8044-eb85-7a2b-e2675ec6f84e.png)


# GitHub Actionsでテスト＆静的解析を自動化
以下の記事を参考に少し変更して、静的解析とテストを自動化しました。
[Flutter の快適 GitHubActions まとめ](https://qiita.com/sensuikan1973/items/cf4626f480cb584a9e50)

プロジェクト直下に`.github/workflows`というフォルダを作成し、その中にyamlファイルを置いていきます。
まずは静的解析用の`flutter_analyze.yaml`です。

```yaml:flutter_analyze.yaml
name: Flutter_Analyzer

on:
  pull_request:
    types: [opened, synchronize]
  push:
    branches:
      - main
      - develop

jobs:
  flutter_analyze:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: subosito/flutter-action@v1
        with:
          channel: 'stable'
      - run: flutter pub get
      - run: flutter analyze
```

`on:`の部分がトリガーになっていて、今回は`pull_request`と`main`、`develop`ブランチへの`push`に対して行うようにしました。

`jobs:`の部分で実際に何を行うかを定義しますが、参考の記事に書いてあったものを大部分流用しています。
timeout-minutesは文字通りタイムアウトを定義するのですが、初め1分で指定したところ解析が終わらずタイムアウトしていたので、適当に10分にしています。
コードが長くなるとその分時間がかかるでしょうから、タイムアウトが発生したらチューニングが必要そうです。

### analyzeでエラー
また、ローカルのflutter analyzeではエラーが出ないのに、GitHubActions上では失敗するという事象が発生しました。
詳しくは以下の記事に載っていますが、`channel`が`beta`になっていたからのようです。
※上記に載せた`flutter_analyze.yaml`は`stable`に修正してますのでローカルの`channel`と合わせるようにしてください。
[Flutter + GitHub Actions で flutter analyze に失敗したときの対処法](https://k-anz.hatenablog.com/entry/2020/04/30/235822)

`channel`はターミナル上で以下のように確認できます。

```cmd:
XXX\AndroidStudioProjects\todo_app_sample_flutter> flutter channel
Flutter channels:
  master
  dev
  beta
* stable
```

変更したければ以下のように打てば良いです。

```cmd:
> flutter channel stable
> flutter upgrade
```

エラーが発生すると以下のように、プロジェクトの「Actions」タブで赤丸の×が表示されます。
![gihubactions_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/f55b3654-2d47-4d3e-f1ef-2fd7cc9c08df.png)

クリックすると詳細が見れます。
![view_state.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/12f1e4b4-10fe-98bb-7b05-4f752b7a7ad5.png)

## テスト自動化
テスト自体の書き方は以下で詳しく書いていますのでご参照ください。
[Flutterで単体テストを書く](https://qiita.com/tokkun5552/items/ede8460bef4892f48e37)
[【Flutter】Providerで最低限のDIを行ってテスタブルなコードにリファクタリングする](https://qiita.com/tokkun5552/items/7af34769104e94f50745)
テストの方は以下のようなyamlとしました。先に結果を示しておきます。

```yaml:flutter_test.yaml
name: Flutter_Test

on:
  pull_request:
    types: [opened, synchronize]
  push:
    branches:
      - main
      - develop

jobs:
  flutter_test:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 1
      - uses: subosito/flutter-action@v1
        with:
          channel: 'stable'
      - run: flutter pub get
      - run: flutter test --no-test-assets --coverage --coverage-path=~/coverage/lcov.info
      - uses: codecov/codecov-action@v1
        with:
          token: ${{secrets.CODECOV_TOKEN}}
          file: ~/coverage/lcov.info
```

純粋に`run: flutter test`でも良いのですが、今回は*codecov*を使ってカバレッジを可視化するところまで自動化しました。

## Codecovでカバレッジ可視化
codecovはテストのカバレッジを可視化できるサービスです。publicリポジトリであれば無料で利用でき、privateでも個数の制限の限りは無料で使えるようです。
[codecov](https://app.codecov.io/)

公式ページのSignUpボタンをクリックします。
![codecov_signup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/24a1b19b-90d1-f92c-2ce0-cb22bea87d30.png)

GitHubのアカウントでログインできるので、そちらを選択すると以下のようなページに飛ぶのでAuthorize codecovを押して許可しましょう。

![auth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/c4176af4-e4c6-2053-2c81-d2e5103679a5.png)

ログインが完了したらrepository選択できる画面が表示されるので、「Add a repository」をクリックします。
![codecov_aft_login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/66ded2a1-e7f1-199a-a5e1-05ec9dd0f8c7.png)

Choose a new repository belowと出てくるので、追加するrepositoryを選択します。
![add_repo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/52bc8d6e-97fa-f2a5-6002-8222d5ab7124.png)


選択出来たら、以下のようにTOKENが発行されますので、GitHubのページに貼り付けるためにコピーしておきます。

![codecov_secrets.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/8fddc968-98a8-7e71-a9ea-98e41d36e039.png)

プロジェクトのSettingsのSecretsを押してNew Repository Secretをクリックすると以下の画面が表示されます。
Nameを適当に設定して、valueの欄に先ほどコピーしたTOKENを張り付けてから「Add secret」をクリックします。

![add_secrets.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/b4b67f85-0a86-6c99-77a8-eb6c81df42eb.png)

準備が整いましたので、トリガーに指定したようにプルリクをオープンするかmain、developにプッシュしてみます。

成功すると、codecov上にカバレッジの情報が表示されます。
以下のようにファイル毎にどこがテストできていないのかなどが可視化されます。

![coverage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/725cc787-8c48-e9fd-4d0d-60ca8851b9d4.png)

## Macであればローカルでも可視化できる
codecovはWindows環境でもMac環境でも利用できますが、Mac環境の場合、lcovというツールを使えば、ローカル環境でカバレッジを可視化することができます。
ターミナル上で以下のコマンドを実行します

```bash:
brew install lcov
flutter test --coverage
genhtml coverage/lcov.info -o coverage/html
```

すると、プロジェクト直下にcoverageというフォルダができています。
展開すると下記の様に、htmlの静的ページが生成されています。
![スクリーンショット 2021-02-21 0.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/207af64d-cbe9-1763-34e6-c77fc38cd8ab.png)

Finderでたどって、index.htmlを開くと以下のようにカバレッジが表示できます。

![スクリーンショット 2021-02-21 0.33.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/6f4dcb5c-a0c7-4092-f66a-5c1f1b6609ec.png)

ファイル毎に細かく表示することができ、どのメソッドのテストが出来ていないかも見ることができます。
![スクリーンショット 2021-02-21 0.35.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141823/64c6eee6-9a41-75d9-e393-4e7dcc81d840.png)

# まとめ
GitHub Actionsを使って静的解析と単体テストの自動化を行いました。
カバレッジの可視化についても思っていたより簡単に実装出来ました。
カバレッジがXX%を超えていなければプルリクを通さない、などの設定も行えるようなので
プロジェクトのルールを決めて運用すると品質を保つのに貢献できそうです。

GitHub Actionsでは他にもデプロイを自動化したりもできるようなので、機会があればまた記事にしようと思います。

# 2021/4/9 追記
こちらに関する発表を行いました。
以下が発表スライドです。
<iframe src="//www.slideshare.net/slideshow/embed_code/key/sYJVD9J7Ve7IaL" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/ShinnosukeTokuda/fluttergithub-actions" title="Flutterで単体テストを行う方法とGitHub Actionsを使った自動化" target="_blank">Flutterで単体テストを行う方法とGitHub Actionsを使った自動化</a> </strong> from <strong><a href="https://www.slideshare.net/ShinnosukeTokuda" target="_blank">ShinnosukeTokuda</a></strong> </div>

### 2021/4/16 追記
2021/1/31～2021/4/15の期間でcodecovに不正アクセスがあったようです。codecovを用いてGitHub Actionsから自動でカバレッジを計測していた場合は、
[こちら](https://qiita.com/tokkun5552/items/2a844a5f87bb0f37f234)の記事を参考にしてセキュリティの問題に対応してください。