VASILYの自動化大好きAndroidエンジニア堀江(@Horie1024)です。今回、GitHubとCircleCIを利用したAWS Device Farmでのテストの自動化ついてご紹介しようと思います。
概要
- Calabashで書いたAndroidアプリのE2EテストをAWS Device Farmで実行する
- GitHub + CircleCIでAWS Device Farmでのテストの実行を自動化した
- Hubotを利用して実行中テストの監視とSlackへの通知をやってみた
- Cloud Test Labを早く試したい
- BazelのロードマップにCloud Test Labのサポートが記載されている
サンプルプロジェクト
サンプルプロジェクトはこちらです。サンプルアプリはLogin画面とHello world!と表示されるだけの画面を持つアプリで、このアプリをテストするCalabashのfeatureはとても簡単に書けます。
以下はローカルでテストを実行した様子です。これをAWS Device Farmで実行します。
GitHub + CircleCIでAWS Device Farmでのテストを自動化
以下の図のような流れで自動化してみました。 1. GitHubへPush
2. GitHubへのPushを検知してCircleCIがビルド開始
3. AWS Device Farmでのテストを実行
4. Slackへテストの開始とHubotへの監視依頼を通知
5. Hubotがテストの監視依頼に反応
6. AWS Device Farmで実行中のテストを監視
7. テストが完了後その結果をSlackに通知
8. 結果を確認 以下各項目の詳細です。
1. GitHubへPush
開発したコードと追加したCalabashのfeaturesをcommitしGitHubへPushします。
2. GitHubへのPushを検知してCircleCIがビルド開始
CircleCI上でAndroidプロジェクトをビルドしAPKを作成します。Androidのプロジェクトをビルドするには以下のようなcircle.ymlを用意し、プロジェクトのリポジトリに追加します。今回はmasterブランチに差分がPushされた場合にビルドされるよう設定します。 これでmasterブランチへ差分がPushされると ./gradlew assembleDebugが実行されAPKが作成されます。
CircleCIの設定などより詳しい内容はこちらの記事をご覧ください。
3. AWS Device Farmでのテストを実行
AWS Device Farmでのテストを実行するまでの流れは以下のようになります。
1. DevicePool情報の取得
2. APKのアップロード
3. テストパッケージのアップロード
4. テストのスケジューリング
これらをAWS SDKを使用して実現します。
APIドキュメントと使用するSDK
ドキュメントは以下を参照しました。
また、SDKにはAWS SDK for Ruby - Version 2を使用します。
AWS SDK Clientの作成
clientを作成します。IAMでAWSDeviceFarmFullAccessのポリシーをアタッチしたユーザーを用意し、そのAccessKeyIdとSecretAccessKeyを使用します。また、regionにはus-west-2を指定します。AccessKeyIdとSecretAccessKeyは環境変数に登録したものを使用するのが良いでしょう。
Device Farmのプロジェクト情報を取得
まず試しにDevice Farmで作成したプロジェクトの情報を取得してみます。プロジェクトを作成していない場合Device Farmのコンソールから「Create a new project」をクリックしプロジェクトを作成しておきます。 プロジェクト情報は以下のコードで取得できます。
devicefarm.get_project({ arn: "プロジェクトのARN" })
ここで、プロジェクトのARNは以下のようになります。
arn:aws:devicefarm:us-west-2:{ユーザーID}:project:{プロジェクトID}
プロジェクトIDは、Device Farmでプロジェクトを開いた際のURLの以下の部分を使用します。
/devicefarm/home?region=us-west-2#/projects/この部分/runs
DevicePoolの取得
デバイスプールについて
テストを実行するデバイスの種類は、デバイスプールと呼ばれる単位で管理されています。デバイスプールをAWS SDKから作成することは出来ますが、コンソールから作成した方がわかりやすいです。 コンソールのプロジェクトページに表示される「Create a new run」をクリックすると新しいRun(テスト対象APKやデバイスの種類、テストケースを紐付けたもの)を作成することができます。そのStep3で任意のDevicePoolを作成できます。 デフォルトで「Top Devices」というDevicePoolが用意されていますので、今回はこちらを使用します。
AWS SDKでのDevicePoolの取得
以下のコードでDevicePoolを取得できます。レスポンスにはDevicePoolのARNなどの情報が含まれます。ドキュメントはこちらです。
APKのアップロード
アップロードは2段階の手順を踏みます。 ・ devicefarm.create_upload()でのinitialize ・ Pre-Signed URLを使用したAPKアップロード
initialize
以下のコードで初期化し、Pre-Signed URLを取得します。また、アップロードするオブジェクトのARNを取得しておきます(実行をスケジューリングする際に使用します)。ドキュメントはこちらです。
create_uploadのレスポンスのシンタックスは以下のようになっています。ドキュメントはこちらです。
{ "Upload": { "Arn": "string", "ContentType": "string", "Created": number, "Message": "string", "Metadata": "string", "Name": "string", "Status": "string", "Type": "string", "Url": "string" } }
statusは、FAILED、INITIALIZED、PROCESSING、SUCCEEDEDの4種類があり、create_uploadを実行した直後は、INITIALIZEDになり、この状態ではDevice Farmから利用できません。Pre-Signed URLを利用してAPKをアップロードすることでPROCESSINGとなり、その後SUCCEEDEDへ変化します。ここでPROCESSINGの際に不具合が起きるとFAILEDになります。
upload
こちらを参考に、以下のようなコードでAPKをアップロードできます。
テストパッケージのアップロード
今回テストにはCalabashを使用するので、Calabashのfeaturesを圧縮後テストパッケージとしてアップロードします。アップロードの手順はAPKのアップロードと同じです。create_uploadのtypeにCALABASH_TEST_PACKAGEを指定します。
テストの実行
実行のスケジューリング
実行のスケジュールは以下のように行います。ドキュメントはこちらです。 app_arnには、APKをアップロードした際に取得したARNを、device_pool_arnはDevicePoolの取得で取得したARNを、test_package_arnにはCalabashのfeatures.zipをアップロードした際に取得したARNを使用します。
APK、テストパッケージのアップロード完了を待つ
APK、テストパッケージをアップロードした直後では、それぞれのstatusがSUCCEEDEDになっているとは限りません。このため、定期的にstatusがSUCCEEDEDかどうか確認する手順が必要になります。 get_uploadを使用すると、uploadしたオブジェクトの状態を取得できるので、それを定期的に取得しstatusを確認します。そして、APK、テストパッケージ両方のstatusがSUCCEEDEDならschedule_runを実行します。
これでスケジューリングされ、Device Farm上でCalabashのテストが実行されます。
CircleCIからのスクリプトの実行
CircleCIからここまでで作成したRubyのスクリプトを実行し連携させます。circle.ymlは以下のように設定します。
ここでdeploy.shは以下のようになります。
これにより、GitHub上のmasterブランチへ差分がPushされるとdeploy.shが実行され、以下の流れでDevice Farmでのテストが自動実行されます。
1. ./gradlew assembleDebug (APKの作成)
2. zip -r features.zip features (テストパッケージの圧縮)
3. ruby scripts/devicefarm.rb (APK、テストパッケージのアップロードとテストのスケジューリング)
4. SlackにHubotへの監視依頼を通知
スケジューリングしたテストは、テスト終了まで時間がある程度かかるためHubotにテストの進捗を監視させます。Hubotへの指令はSlackを経由して行い、Slackへの通知にはSlackのIncoming WebHooksを利用します。 Incoming WebHooksの設定はSlackの「Configure Integrations」からIncoming Webhooksを選択します。
選択すると以下のような画面に遷移しますので、Incoming WebHooksをpostするchannelを選択し、「Add Incoming WebHooks Integration」をクリックします。
これでWebhook URLを取得できるので、そのURLに対してPOSTでHTTPリクエストを送ります。また、Device Farm上でのテストの実行状況を監視するには、実行中のテストのARNが必要になります。
このため、RubyスクリプトからスケジューリングしたRunのARNをSlackに通知します。 今回、Hubotに監視依頼するフォーマットを@vasilybot ARNと決定します。 以下SlackにIncoming WebHooksで通知を送るコードです。
このSlackへの通知のコードを含めた最終的なスクリプトのサンプルはこちらになります。
5. Hubotがテストの監視依頼に反応
CircleCIからSlackのIncoming WebHooksで送られてくるメッセージにHubotを反応させます。
Incoming WebHooksで送られてくるフォーマットは@vasilybot ARNになるので、Hubot側のコードは以下のようになります。これでSlackに@vasilybot ARNと投稿された際にHubotが反応するようになります。
6. AWS Device Farmで実行中のテストを監視
AWS SDKを使用し、定期的にテストの状態を確認します。SDKにはAWS SDK for JavaScriptを使用します。 Access Key IdとSecret Access Keyについては、Configuring the SDK in Node.jsを参考に環境変数にそれぞれ
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
として登録します。これでSDKが自動的にAccess Key IdとSecret Access Keyを参照してくれます。 getRunを使用することで実行中のテストの状態を取得できますので、Hubot側のコードは以下のようになります。
7. テストが完了後その結果をSlackに通知
getRunでテストの実行状態を監視し、statusがCOMPLETEDならSlackへ結果を送信します。また定期実行をclearIntervalでキャンセルします。 最終的なHubot側のコードは以下の通りです。Slackに送信する内容については、Advanced Message Formattingを利用してテスト実行結果詳細へのリンクを付けるなど、より見やすくする工夫をしても良いと思います。
8. 結果を確認
Slackにテストの結果が表示されるので確認します。
Device Farmのコンソールを確認するとテストがパスされています。
まとめ
GitHub + CircleCIを利用してAWS Device Farmでのテストの実行を自動化しました。AWS Device Farmは便利なサービスですが、プロジェクトやRunの削除が出来ない、実行中のRunのキャンセルが出来ないなどまだまだ発展途上と感じる部分も多いです。
AWS Device Farmのようなモバイルアプリのテストをクラウド上で実行する環境を提供するサービスは複数あり、AWS Device Farmも、今年7月にAmazonがAppThwackを買収して始めたサービスです。同様なサービスには、Scirocco CloudやTestmunkがあります。さらに今年のGoogle I/Oでは、Googleが恐らく同様なサービスと思われるCloud Test Labの発表を行っています(事前登録に申し込みましたが今のところ連絡無し)。 Cloud Test LabはAndroidStudioとの連携も考えられているようですし、BazelのロードマップにCloud Test Labのサポートが入っており、非常に楽しみです。利用可能になり次第試してみようと思います。
最後に
VASILYでは、自動化や新しい技術が大好きなエンジニアを募集しています。少しでもご興味のある方は是非こちらからご応募よろしくお願いいたします。