AWS Device Farm で androidTest(Espresso, etc.) が実行できる
動機
現在各種 CI サービスが Android のテストに対応するようになり、CI サービス上で androidTest を実行する方法も共有されている。しかし、ARM エミュレータ(Android SDK がデフォルトで立ち上げるあのもっさりしたエミュレータ)なので Espresso で Activity の挙動についてテストした場合、実機や Genymotion と比べてタイムアウトが発生しやすいという問題がある。
前回の DigitalOcean に Android x86 Atom エミュレータを飼うと ADB の接続をプロキシする方法で、CI が実行する Activity のテストをタイムアウトさせないようにすることを考えた。だがここで一旦原点に立ち返り、実機を従量課金でテストターゲットとして利用できるサービスを CI に組み込めないか検討する。
AWS Device Farm とは
紹介記事は適当にググってほしい。平たく言えば実機を従量課金で借りられるサービス。
標準提供の Fuzz テスト(所謂モンキーテスト)を試してみた系やスクリーンショットを撮ってみた系の記事は多いが、注目するべきは Instrumentation Test(androidTest) が実行できることである。
つまりすでに Espresso などを使って Activity のテストを書いていた場合、Device Farm の利用を今までの Android アプリ開発フローに組み込めるということである。さらに言えば AWS 提供サービスなので他のサービスと同様に API が提供されており、aws-cli による操作も可能である。
このような視点に基けば、端末の種類の少なさは特に問題でもなく、Instrumentation Test を自動で、かつ確実に実行できる基盤として有用であると言える。リファレンス機の Nexus 5 が提供されているので、端末はこれだけでも良いかもしれない。Appium や DSL での E2E テストを各種端末で実行できるという類似サービスもあるが、それらとはカバー範囲が異なるというのが私見である。
もちろん Android 端末細分化や端末ごとの特殊な状況という問題は残るが、今回解決する問題とはレイヤーが異なるのでここでは置いておく。
AWS Device Farm の各リソースのざっくりとした説明
- Project: 文字通り Project である。全く違うアプリをテストしても、テスト履歴が混ざってしまう程度の問題にしかならない。
- Upload: テスト対象のアプリ、テストコードの入ったアーカイブ、テスト用データの入ったアーカイブなどのファイル情報。そのファイルがどういったものかはこちらで指定する必要が有る。
- Device Pool: テストを実行する端末の集合。
- Run: 実際のテスト実行。Upload されたアプリのうちどれを対象に、Upload されたテストコードの入ったアーカイブのうちどれで、どの Device Pool 上でテストするかを指定することで、テストが実行される。
作業
画面の詳細は他の AWS サービスほど複雑ではないので省略。まずはテスト対象となる Android アプリとテストコードを作る。
サンプルというほどのものでもないが、HackerNews の上位 30 件をリストで出すアプリと、初回読み込みの結果がちゃんと 30 個の View として存在するかを確認するテストを作った。
$ ./gradlew assembleAndroidTest
コマンドを実行すると、通常 Android Studio から端末やエミュレータにインストールされる .apk ファイルと、テスト実行時にインストールされる .apk ファイルが生成される。
$ tree app/build/outputs/apk app/build/outputs/apk ├── app-debug-androidTest-unaligned.apk ├── app-debug-unaligned.apk ├── app-debug.apk └── app-release-unsigned.apk
このうち、テスト対象として app-debug.apk を、app-debug-androidTest-unaligned.apk をテストコードとして AWS Device Farm にアップロードする。
注意するべきは Configure Test のステップで Instrumentation を選択しておくこと。これはドキュメントを読めばすぐにわかる。
また、一度作った Project は削除できないという大変男らしい仕様になっているので、この点も要注意である(サポートに問い合わせれば削除してもらえるらしい)。
さらに注意点があり、テストコードは仮に JUnit4, Espresso を使う構成(AndroidInstrumentationTest2 を継承しない、@Test
アノテーションを使う)にしていても、テストクラス名を Test で終わるようにして、テストメソッド名が
test からはじまるように(JUnit3 方式)しないと、Device Farm 側でテストとして認識してくれない(しかし TestCase を継承する必要はない)。Device Farm 側のテストランナーが独自なのかもしれないが、この情報はフォーラムにしか書かれていなかったので難儀した。
あとはどの Device Pool を使うか、端末ごとの環境設定を済ませると、テストが実行される。単純に androidTest を実行するだけであれば、特に細かい設定を変更するようなことはない。
所感
これまで Android の UI テスト(結合テスト)を CI することは、種々の制約による障壁が存在した。
- 実機を USB 接続するなら Jenkins の面倒を見る必要がある
- 実機の場合、テスト毎にクリーンな環境を作らなければならない
- 何より実機を購入するためのコストがかかる
- CI サービスのエミュレータではタイムアウトが発生する
特に CI のために実機を用意することは個人開発者レベルではコスト的に難しかったので、比較的少額でテストを自動実行できるサービスが出現したということはありがたい。
ただ現時点では、テスト実行をスケジュールしてから完了まで一時間程度待つことがあったり、テストの完了を通知する仕組みがなかったり、まだ粗削りなサービスだと感じられた。
また、すでに稼働していて、テストの資産がある程度存在するプロジェクトに CI 要素として組み込んだとき、予期しないタイムアウトが発生しないか、テスト実行時間が現実的かなど、他にも考慮する点はある。