Androidアプリの開発において Github Actionsにて自動的にビルドする環境作成 について紹介しました。
またビルドが完了した後にFirebase App Distributionにアプリを配布する方法についてはGithub Actionsで自動的にAndroidアプリをビルドした後にFirebase App Distributionに配布するにて紹介しています。
今回はFirebase Test Lab を使用して、開発したAndroidアプリのテストを自動的に行う環境(CI環境)の構築について紹介していきます。
実際に自動テストを行なっている様子は以下のプロジェクトにて公開しています。
github-actions-examples
Github Actionsで自動テストを行なっている実行フローのレシピはこちらに記述しています。
モチベーション
ネイティブアプリにおける自動テストの導入を行いたい。しかしネイティブアプリの自動テストを行うには実機またはエミュレーターが必要なためGithub Actions内でのみで解決することは難しいです。そのためFirebase Test LabなどのSaaSと組み合わせることによって、この問題の解決ができそうなため、実装してみました。
実行フロー
Github Actionsで実際に実行されるフローの様子について以下に図示します
今回、紹介するフローは上記の「自動テストの実行」の部分について紹介していきます
Androidアプリのテストについて
Androidアプリにおいて自動テストを行う場合以下の2種類のテストが存在します
- ローカル単体テスト(別称: ユニットテスト) (以降 UnitTestと表記)
- インストゥルメント化単体テスト(別称: UIテストなど) (以降 InstrumentationTestと表記)
UnitTest
AndroidにおけるUnitTestはAndroid SDKの機能を用いない状態で実行されるテストのことをいいます。
つまり実装の中で以下のようなものが import
されていないテストがUnitTestになります。
import android.*
import androidx.appcompat.*
テスト処理の記述についてはAndroidプロジェクト内の app/src/test/java/
以下に処理の内容を記述します。
こちらのサンプルのようにテストを記述しています。
ExampleUnitTest.kt
より詳しいUnitTestを記述する環境の構築についてはこちらを参考にしてください。
InstrumentationTest
InstrumentationTestとはAndroid SDKの機能を用いた上で実行されるテストのことをいいます。
つまり実装の中で以下のようなものがimport
されているテストがInstrumentationTestにあたります。
import android.*
import androidx.appcompat.*
Android SDKの機能を用いているため、実機のAndroid端末またはAndroidエミュレーター上でのみテストを実行させることができます。
Androidでテストを記述する場合、ほぼ全てのテストがInstrumentationTestとして記述します。
テスト処理の記述については Androidプロジェクト内の app/src/androidTest/java/
以下に処理の内容を記述します。
こちらのサンプルのようにテストを記述しています。
ExampleInstrumentedTest.kt
より詳しいInstrumentationTestを記述する環境の構築についてはこちらを参考にしてください。
Firebase Test Lab
Firebase Test Lab とは?
Firebase Test Lab はFirebaseの中の機能の一つです。
Firebase Test Lab を使用することによってUnitTestだけでなく、実機やエミュレーターを使用してのInstrumentationTestの実行も可能です。
Firebase Test Lab でのテストの実行は仮想デバイステストの場合は1日10回まで、実機テストの場合は1日5回まで無料で実行できます。詳しくは こちら を参照してください。
Firebase Test Lab で実行できるテストには以下の2種類があります。
- ロボテスト(RoboTest)
- インストルメンテーションテスト(InstrumentationTest)
RoboTestはAPKのみで実行可能なテストで主にアプリを起動し、自動的に適当に動かしてみてクラッシュしたりしないかどうか確認するテストです。
InstrumentationTestは上記のInstrumentationTestと同様のものを実行するテストです。
Firebaseプロジェクトにアプリを登録する
Github Actionsで自動的にAndroidアプリをビルドした後にFirebase App Distributionに配布する にて紹介しているFirebaseプロジェクトにアプリを登録すると同様のことを行いますのでそちらを参照してください。
Firebase Test Lab でテストを実行する端末の確認
Firebase Test Lab にて実行することが可能なAndroid端末、エミュレーターやOSバージョンを確認します。(後でテストを実行するときに指定します)
- FirebaseのプロジェクトからTest Labを選択してコンソールを開きデバイスカタログを選択します。(またここではこれまでのテストを実行した結果の一覧も確認することができます)
- デバイスの一覧よりテストで使用する端末とOSのバージョン(APIレベル)を確認する
使用したいデバイスの情報を後述の実行する時のコマンドにて指定することでテストの実行が可能になります。
また、CLI経由でも端末やOSのラインナップの情報を確認することができます。
後述のgcloudの設定が完了している状態で
- 以下のコマンドを実行することで利用可能なデバイスの一覧を確認できます
gcloud firebase test android models list
- 以下のコマンドを実行することで利用可能なOSバージョンのリストを確認することができます。
gcloud firebase test android versions list
- 以下のコマンドを実行することで利用可能なロケールのリストを確認することができます。
gcloud firebase test android locales list
Firebase Test Lab でテストを実行する
Firebaseプロジェクトにアプリを登録することができたら各種設定を行なった上でFirebase Test Labにてテストの実行することができるようになります。
以下の手順をそれぞれ実行していきます。
-
google-service.json
ファイルを設置する
アプリを登録したときに案内にでてきたもの使用します。
「google-service.jsonをダウンロード」を選択してgoogle-service.json
ファイルをダウンロードし、案内にあるようにAndroidプロジェクトのapp/google-service.json
の場所に設置する
-
gcloud CLI をインストールする ← リンク先を参考にgcloud CLIを実行できるようにインストールする
-
gcloud CLI を使用して設定する(以降のコマンドを順次実行していきます)
- インストールが最新であることを確認する
gcloud components update
- Googleアカウントにgcloud CLIでログインします
gcloud auth login
- gcloud で Firebase プロジェクトを設定します。
gcloud config set project PROJECT_ID
この時の PROJECT_ID
はFirebaseプロジェクトの「プロジェクトの設定」を開き
「プロジェクトID」の確認し、その値を指定することで設定することができます。
詳しくは gcloudCLIでテストを開始します を参照してください。
- 通常のAndroid Buildをコマンドで行う
./gradlew assembleDebug
- 加えて以下のコマンドを実行して、Androidのテスト実行用のAPKをビルドして生成していきます
./gradlew assembleAndroidTest -DtestBuildType=debug
- コマンドを実行して、Firebase Test Lab にてRoboTestを実行する
gcloud firebase test android run --type robo --app [APKファイルのパス] --device "model=[テストを行うデバイスのモデル名(端末名)],version=[テストするOSのバージョン],locale=[テストする端末の言語設定]"
- コマンドを実行して、Firebase Test Lab にてInstrumentationTestを実行する
gcloud firebase test android run --type instrumentation --app [APKファイルのパス] --test [テスト実行するAPKが格納されているディレクトリのパス] --device "model=[テストを行うデバイスのモデル名(端末名)],version=[テストするOSのバージョン],locale=[テストする端末の言語設定]"
Firebase Test Lab でテストを実行するための権限設定
上記のCLIの設定手順ではローカルでの実行は可能ですがGithub Actionsなどの環境においては実行を試みるも失敗してしまいます。
そこでGithub Actionsで実行可能にするために
- 専用のサービスアカウントの情報を取得する
- サービスアカウントに「編集」の権限を加える
をそれぞれ行う必要があります。
専用のサービスアカウントの情報を取得する
- Firebaseプロジェクト内のサービスアカウントからGoogle Cloudの「IAMと管理」の画面を開く
- サービスアカウントを選択して、すでに作成されているfirebaseプロジェクト専用のサービスアカウントを確認する。
- サービスアカウントの詳細を開く
- キーを選択して新しい鍵を作成する
- JSONを選択して作成を選択する
サービスアカウントのキーを作成するとJSONファイルのダウンロードが行われる。このJSONファイルの中身は後述するGithub Actionsの実行で使用するためにGithub Secretsに登録する(${{ secrets.FIREBASE_CREDENTIALS_JSON }}
で呼び出しているものがここで登録したJSONファイルの中身に相当する)
サービスアカウントに「編集」の権限を加える
- IAMを選択して、firebaseプロジェクトが作成されていれば専用のサービスアカウントのロールを確認する。このサービスアカウントのロールに「編集者」の権限を追加する
- 「編集者」の権限を追加されているのを確認して、保存を選択する
※ 今回、「編集」の権限を付与することによってFirebase Test Labの実行を可能にしていますがこの権限は余分な権限を多く付与してしまっています。ここではどの権限を付与するのが適切なのか判別がつかなかったため、もしご存知の方がいましたら教えていただけますと幸いです。
Github Actionsの中に組み込む
Github Actionsでは gcloud
のセットアップは独自に行うのではなく、提供されているものを利用する(自前でセットアップする場合にはGoogleアカウントにgcloud CLIでログインする認証を通すのが結構難しい)
レシピの中の steps
の部分にて以下のようにgcloud CLI をインストールする こととサービスアカウントで認証を通しています。
steps:
- uses: 'google-github-actions/auth@v0'
with:
credentials_json: '${{ secrets.FIREBASE_CREDENTIALS_JSON }}'
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@v0'
このとき、${{ secrets.FIREBASE_CREDENTIALS_JSON }}
には上記権限を持たせたサービスアカウントの情報が記述されているJSONファイルの中身が代入されます。
gcloudの設定が完了したらあとは以下の手順をそれぞれレシピに追記していき、それぞれ実行されるように設定していきます。
-
google-services.json
の中身をこれからビルドするAndroidプロジェクトに設置する
steps:
- name: setup google-services.json
run: |
echo "${{ secrets.GOOGLE_SERVICES_JSON }}" >> ./app/google-services.json
- APKをビルドするコマンド
steps:
- name: Android Debug Apk Build
run: ./gradlew assembleDebug
- テスト用APKをビルドするコマンド
steps:
- name: Android Debug Test Apk Build
run: ./gradlew assembleAndroidTest -DtestBuildType=debug
- testを実行するFirebase プロジェクトIDを設定する
steps:
- name: Set execute project id
run: gcloud config set project ${{ secrets.FIREBASE_PROJECT_ID }}
- Firebase Test Lab にてRoboTestを実行する
steps:
- name: Execute firebase robo test
run: gcloud firebase test android run --type robo --app app/build/outputs/apk/debug/app-debug.apk --device "model=Pixel3,version=30,locale=ja_JP"
- Firebase Test Lab にてInstrumentationTestを実行する
steps:
- name: Execute firebase instrumentation test
run: gcloud firebase test android run --type instrumentation --app app/build/outputs/apk/debug/app-debug.apk --test app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk --device "model=Pixel3,version=30,locale=ja_JP"
この時、権限を持たせたサービスアカウントの情報(${{ secrets.FIREBASE_CREDENTIALS_JSON }}
)とgoogle-services.json
の中身の情報(${{ secrets.GOOGLE_SERVICES_JSON }}
)、(ついでにFirebaseのプロジェクトIDの情報(secrets.FIREBASE_PROJECT_ID
))は公開したくない情報であるためGithub Secretsに登録して、登録した値が参照されるように設定します。
課題点
Github ActionsにてFirebase Test Labを実行する環境を構築し、運用していくにあたり以下の課題があります。
- Firebase Test Lab を実行した場合、結構な時間がかかる(数十分) → Github Actionsでの実行時間によるコストが増大する
- Firebase Test Lab には1日5回までの実行の無料枠はあるものの、基本的に実行するランニングコストが高い
上記のような課題があるため、Firebase Test Labを実行して自動テストが行うブランチには制限をかけて運用していくことをおすすめします。(例えばmaster
やmain
ブランチにmergeされたときにのみ実行されるなど)
まとめ
Github ActionsでFirebase Test Labを使ってAndroidアプリの自動テストを行う環境の構築について紹介しました。
少し癖があり、環境の構築のハードルは高いですが、Androidアプリの開発におけるCI環境の導入に役立ててみてください。