CircleCIからFirebase Test Labでのテストを実行する


# 概要

[Firebase Test Lab](https://firebase.google.com/docs/test-lab/)はクラウドベースのアプリテスト基板です。この投稿ではCircleCIからFirebase Test LabでのAndroidアプリのテストを実行する方法についてまとめます。

# サンプルアプリ

Firebase Test Labでテスト実行するサンプルアプリとして以下を用意しました。2Activityで構成される簡単なアプリで、テストコードをEspressoで書いています。

https://github.com/horie1024/FirebaseTestLabSample

# CLIからのFirebase Test Labによるテストの実行

まずローカル環境でCLIからFirebase Test Labによるテストを実行できるようにします。その流れとしては以下のようになります。

1. Firebaseプロジェクトの作成と課金
2. Google Cloud SDKの実行環境の構築
3. テストの実行

これらの詳細は以下の記事にまとめています。

http://qiita.com/Horie1024/items/ba387e1861e12fdca0b6

# CircleCIからのFirebase Test Labでのテスト実行の流れ

ローカル環境からCLIを使用してテストを実行できたのでCIのプロセスへ組み込みます。CIが実行される流れとして今回は以下のような流れを想定します。

1. コードの変更をコミットしリポジトリにプッシュ
2. CircleCIが変更を検知しビルドを開始
3. Firebase Test Labによるテストの実行

Firebase Test Labによるテストの実行の部分を分解すると以下のようになります。

1. コードの変更をコミットしリポジトリにプッシュ
2. CircleCIが変更を検知しビルドを開始
3. gcloudコマンドラインツールの設定、認証処理
4. apkおよびテストapkの作成
5. Firebase Test Labによるテストの実行

この内、gcloudコマンドラインツールの認証処理は、CLIで実行した場合と異なります。

# CircleCIでのgcloudコマンドラインツールの認証処理

## 認証方法

CircleCIでのgcloudコマンドラインツールの認証には、サービスアカウントキーを使用します。サービスアカウントキーは、JSONとP12形式を選択できますが、今回はキーをJSONファイルとしてダウンロードします。

## サービスアカウントの作成とキーのダウンロード

サービスアカウントキーを作成するには、[こちらのリンク](https://console.developers.google.com/projectselector/apis/credentials/serviceaccountkey)からGoogle Cloud PlatformのAPIマネージャーを開き、先ほど作成したFirebaseプロジェクトを選択します。

![test_lab_1.png](https://qiita-image-store.s3.amazonaws.com/0/43438/b54661ed-555b-0915-a8f3-bb3369822d87.png "test_lab_1.png")


次にサービスアカウントを作成します。`サービスアカウント名`と`サービスアカウントID`を入力して新規作成します。`役割`には、テスト結果を保存する際にGoogle Cloud Storageへのアクセスが必要になるので`編集者`を指定します。サービスアカウントの役割についての詳細は、[こちら](https://cloud.google.com/iam/docs/understanding-service-accounts?hl=ja)から確認できます。そしてキーのタイプに`JSON`を選択し`作成`をクリックするとJSONファイルがダウンロードされます。

![test_lab_2.png](https://qiita-image-store.s3.amazonaws.com/0/43438/36e3c51b-0e74-f484-c1ef-eba1c0ce57f0.png "test_lab_2.png")

サービスアカウントキーは認証情報ですので扱いには十分注意してください。

## 必要なAPIの有効化

`Google Cloud Testing API`と`Cloud Tool Results API`を有効にします。APIを有効にするためには、コンソール上部の検索窓にAPI名を入力し、APIの概要ページで「有効にする」をクリックします。有効化後、「すべてのAPI認証情報」にサービスアカウントキーを作成したサービスアカウントが表示されていることを確認します。

![test_lab_3.png](https://qiita-image-store.s3.amazonaws.com/0/43438/f0fc033a-a83a-db32-3126-96e6936dc1f9.png "test_lab_3.png")

## CircleCIのセットアップ

### プロジェクトの追加

CircleCIとGitHubリポジトリを紐付けます。例として、サンプルとして用意した「[FirebaseTestLabSample](https://github.com/horie1024/FirebaseTestLabSample)」をプロジェクトとして追加します。organizationを選択し、リポジトリ名の右側に表示されている「Build Project」をクリックすれば最初のビルドが開始されます。

![test_lab_4.png](https://qiita-image-store.s3.amazonaws.com/0/43438/84eb9b08-e9dd-05a0-ecf2-30dbfb565521.png "test_lab_4.png")

### CircleCIへのサービスアカウントキーの追加

以前のセクションでサービスアカウントキーをJSONファイルとしてダウンロードしたと思います。CircleCIがgcloudを認証するために、CircleCIはサービスアカウントキーへアクセスする必要がありますが、このキーをバージョンコントロール下で直接管理するのはセキュリティ的にスマートなやり方ではありません。直接管理する代わりにCircleCIの環境変数を利用して認証させます。

まず最初にサービスアカウントキーをbase64でエンコードし、その結果をコピーします。

```bash
$ base64 <your-service-account.json>
```

先ほど追加したプロジェクトが左側のBUILDSから確認できるので選択します。プロジェクト名の右側もしくは右上にある`Project Settings`をクリックします。

![test_lab_5.png](https://qiita-image-store.s3.amazonaws.com/0/43438/616a99ac-10d9-8748-3a25-eabb70ea6528.png "test_lab_5.png")


設定メニューが表示されるので`Environment variables`を選択し、`Add Variable`をクリックします。

![test_lab_6.png](https://qiita-image-store.s3.amazonaws.com/0/43438/b25f098c-0eac-d806-f430-ea5890a24dc9.png "test_lab_6.png")


Nameに`CLIENT_SECRET`、Valueにコピーしたエンコード済みのサービスアカウントキーの値をペーストし、`Save variables`をクリックします。これでCircleCIのビルドからサービスアカウントキーへアクセスできるようになりました。

![test_lab_7.png](https://qiita-image-store.s3.amazonaws.com/0/43438/5d2b6007-cfce-15cb-feab-f5e139627bb6.png "test_lab_7.png")

### gcloudコマンドラインツールの認証

環境変数に登録したサービスアカウントキーは、ビルドスクリプトでJSONファイルにデコードします。

```bash
$ echo $CLIENT_SECRET | base64 --decode > ${HOME}/client-secret.json
```

そして`client-secret.json`を使用してgcloudコマンドラインツールの認証を行います。

```bash
$ gcloud auth activate-service-account --key-file ${HOME}/client-secret.json
```

# circle.yml

CircleCIからgcloudコマンドラインツールの認証を行う準備ができました。今回、CIが実行される流れを以下のように想定していますので、この流れを実現するようcircle.ymlを定義していきます。

1. コードの変更をコミットしリポジトリにプッシュ
2. CircleCIが変更を検知しビルドを開始
3. gcloudコマンドラインツールの設定、認証処理
4. apkおよびテストapkの作成
5. Firebase Test Labによるテストの実行

1,2に関してはリポジトリをCircleCIに追加した時点で既に実現できているので、circle.ymlで定義するのは3,4,5についてです。

## dependenciesセクション

dependenciesセクションでは、サンプルアプリのビルドに必要なAndroid SDKのアップデートとgcloudコマンドラインツールの設定、認証処理を行います。

サンプルアプリでは、compileSdkVersionを24、buildToolsVersionを24.0.2に指定し、加えてsupport libraryを利用しているのでAndroid SDKのアップデートは以下のようなコマンドになります。

```bash
dependencies:
  override:
    - echo y | android update sdk --no-ui --all --filter "android-24,build-tools-24.0.2"
    - echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
```

また、gcloudコマンドラインツールの設定、認証処理は以下のようになります。`gcloud config set project`でセットしているプロジェクト名は環境変数PROJECT_IDとして定義しています。環境変数CLIENT_SECRETに格納したサービスアカウントキーをclient-secret.jsonにデコードし認証に使用します。`--quiet`オプションを指定すると全てのインタラクティブなプロンプトを無効にします。

```bash
dependencies:
  override:
    - echo $CLIENT_SECRET | base64 --decode > ${HOME}/client-secret.json
    - sudo /opt/google-cloud-sdk/bin/gcloud config set project $PROJECT_ID
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components update
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components install beta
    - sudo /opt/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file ${HOME}/client-secret.json
```

最終的なdependenciesセクションの設定は以下のようになります。

```bash
dependencies:
  override:
    - echo y | android update sdk --no-ui --all --filter "android-24,build-tools-24.0.2"
    - echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
    - echo $CLIENT_SECRET | base64 --decode > ${HOME}/client-secret.json
    - sudo /opt/google-cloud-sdk/bin/gcloud config set project $PROJECT_ID
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components update
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components install beta
    - sudo /opt/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file ${HOME}/client-secret.json
```

## testセクション

testセクションで行うことは、apkおよびテストapkの作成とFirebase Test Labによるテストの実行です。apkおよびテストapkの作成するには、`assembleDebug`タスクと`assembleAndroidTest`タスクを実行します。

```bash
test:
  override:
    - ./gradlew assembleDebug assembleAndroidTest
```

そして、`gcloud beta test android run`コマンドでFirebase Test Labでのテストをスケジューリングします。

```bash
test:
  override:
    - ./gradlew assembleDebug assembleAndroidTest
    - gcloud beta test android run --type instrumentation --app app-debug.apk --test app-debug-test.apk --device-ids Nexus5 --os-version-ids 21 --locales ja_JP --orientations portrait
```

テストに必要なパラメーターは、上記のように`gcloud beta test android run`コマンドのオプションとして指定することも出来ますが、見通しが悪いのでYAMLファイルにまとめてしまいます。

```bash:test_settings.yml
instrumentation-test:
  type: instrumentation
  app: app/build/outputs/apk/app-debug.apk
  test: app/build/outputs/apk/app-debug-androidTest.apk
  device-ids: Nexus5
  os-version-ids: 21
  locales: ja_JP
  orientations: portrait
```

オプションとしてYAMLファイルに定義した値を使用した場合、`args.yaml:instrumentation-test`の形式で指定します。YAMLファイルへの定義方法については`gcloud topic arg-files`を実行することで確認できます。

```bash
test:
  override:
    - ./gradlew assembleDebug assembleAndroidTest
    - echo "y" | sudo /opt/google-cloud-sdk/bin/gcloud beta test android run test_settings.yml:instrumentation-test

```

## CircleCIのartifactsへのテスト結果の保存

`gsutil`を使用することでCircleCIのartifactsにGoogle Cloud Storageのbucketに保存されたテスト結果の内、最も新しいファイルをダウンロードすることができます。これによって、Google Cloud StorageとCircleCIの両方でテスト結果を利用することができます。

最終的なcircle.ymlは以下のようになります。

```bash:circle.yml
dependencies:
  pre:
    - sudo pip install -U crcmod
  override:
    - echo y | android update sdk --no-ui --all --filter "android-24,build-tools-24.0.2"
    - echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
    - echo $CLIENT_SECRET | base64 --decode > ${HOME}/client-secret.json
    - sudo /opt/google-cloud-sdk/bin/gcloud config set project $PROJECT_ID
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components update
    - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components install beta
    - sudo /opt/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file ${HOME}/client-secret.json
test:
  override:
    - ./gradlew assembleDebug assembleAndroidTest
    - echo "y" | sudo /opt/google-cloud-sdk/bin/gcloud beta test android run args.yaml:instrumentation-test
  post:
    - sudo /opt/google-cloud-sdk/bin/gsutil -m cp -r -U `sudo /opt/google-cloud-sdk/bin/gsutil ls gs://${BUCKET_NAME} | tail -1` $CIRCLE_ARTIFACTS/ | true
```

テスト結果はスクリーンショットや以下のように動画で確認できます。

https://youtu.be/S8sMcp1XJEo

# まとめ
CircleCIからFirebase Test LabでのAndroidアプリのテストを実行することができました。CircleCIでは、gcloudやgsutilといったコマンドが予めインストールされているため連携がしやすかったです。また、ビルドの実行時間は大体11分程度でした。CircleCIでのビルドの詳細は[こちら](https://circleci.com/gh/horie1024/FirebaseTestLabSample)から確認できます。

# 参考資料
- [Using Firebase Test Lab for Android from the gcloud Command Line](https://firebase.google.com/docs/test-lab/command-line)
- [Using Firebase Test Lab for Android with Continuous Integration Systems](https://firebase.google.com/docs/test-lab/continuous)
- [Testing with Google Cloud Test Lab](https://circleci.com/docs/google-cloud-test-lab/) ([日本語訳](http://qiita.com/Horie1024/items/0eb13b09d88129de8ea0))
- [Authentication with Google Cloud Platform](https://circleci.com/docs/google-auth/) ([日本語訳](http://qiita.com/Horie1024/items/54fd0611a6def88fde22))