Maestro + self-hosted runners でFlutterアプリのE2Eテストを実行する

Maestro + self-hosted runners でFlutterアプリのE2Eテストを実行する

背景

Awarefyでは「アプリにE2Eテストを導入したい」という動きはあったものの、我々に合うツールをなかなか見つけられず、導入まで至っていませんでした。しかし5/9の大型アップデートを終えたタイミングで改めてE2Eテスト導入プロジェクトを動かし、Maestroというフレームワークを用いて、GitHub ActionsからE2Eテストを実行することにしました。

Maestroとは

Maestro is the simplest and most effective mobile UI testing framework.
What is Maestro? | Maestro by mobile.dev
Maestro is the simplest and most effective mobile UI testing framework.

MaestroはUIテストのためのフレームワークです。

Built-in tolerance to delays.

とあるように、私たちが意識しておかなくても遅延実行をしたり、ボタンタップやテキストの入力などのアクションを実行したりしてくれるため、多くの場合でUIテストとしてだけでなく、E2Eテストとして用いられているようです。テスト結果を動画として残してくれることも、その理由のひとつかもしれません。

Maestroの特徴としてはこのほかに、テストをyamlで書くことや、記述のサポートとしてMaestro Studioを利用できることが挙げられます。Maestro Studioは、起動中のiOS SimulatorやAndroid Emulatorをキャプチャし、テストケースを作成してくれます。Maestroでテスト実行するためのコマンドはこちら

Maestroは公式のドキュメントが充実していることも魅力のひとつです。導入の際は公式ドキュメントを見ることで、多くのケースに対応できます。

導入

私たちは最終的なゴールとして、PRのオープンやマージを検知し、E2Eテストを実行することを考えていました。MaestroにはMaestro CloudというCI用のツールが用意されていますが、我が社にはかわいいビルド用マシン、Mac miniちゃんがいるので、self-hosted runnersで実行することにしました!

※ 🧰現状は「PRのオープンやマージを検知し、E2Eテストを実行すること」はしていません🔧

実行の流れ

今回導入したE2Eテストは、以下のような流れで実行するようにしました。

  1. GitHub Actionsで実行環境を入力してrun
  2. ローカルマシンの Mac mini でipa/apkファイルをビルド
  3. ローカルマシンのiOS Simulator/Android Emulatorに 2 でビルドしたアプリをインストール
  4. maestro testまたは maestro record 実行
実行環境の入力フォーム

Maestroの実行で工夫したところ

基本的には、公式ドキュメントを読むことでテストを作成、実行できたのですが、いくつか手こずる場面がありました。Tipsとして残しておきます。

Maestro Studioの活用

BottomNavigationBarのタブタイトルの内容に対してのテストケースを作成し、表示されているテキストを指定しましたが、テストが通りませんでした。

具体的に見てみると、私たちが提供するアプリのアウェアファイには5つタブがあり、それぞれのタブタイトルの表示をテストするように下記のテストケースをyamlに定義しました。

- assertVisible: ホーム
- assertVisible: さがす
- assertVisible: AI
- assertVisible: ログ
- assertVisible: 分析

しかし、このテストケースではテストが失敗したため、Maestro Studioで確認すると以下のようになりました。

BottomNavigationBarのテストをMaestro Studioを使って書いてみる

アウェアファイの場合、他の場面で「思ったテストケースの書き方と違う...」という箇所は今のところないので、特殊な表現がどのくらいあるかは不明ですが、初めてテストを通して失敗したときに確認してみることで、謎にハマることを防ぐことができるはずです。

また、テストの一連の動作の中で、プレースホルダのないTextFieldを入力状態にしたい時、TextFieldのあるポイントに対してタップすることで、入力状態にしました。

横幅、縦幅に対する比率で指定するので、どの位の位置にあるのか、なんとなくは分かりますが、Maestro Studio上で対象となる箇所を選択することで、ポイントを教えてくれます。

Maestro Studioを使ってタップするポイントを見つける

このようにMaestro Studioを活用することで、Maestroを初めて扱う私でも簡単にテストを作成することができました。

Semantics の指定

同じ画面の中に、ラベルとボタンで同じ文言が使われており、ボタンをタップさせたいのにラベルをタップしていることがありました。

この問題がログイン画面で起きていたため、ボタンに Semantics.identifier を指定することで、テストの実行を可能にしました。また、ここでテスト対象だったボタンはアプリ全体の重要なアクションに対して使用するWidgetだったため、ドキュメントの例に倣い、Widgetのコンストラクタで指定するようにしました。

Flutter | Maestro by mobile.dev

実行環境による差異をなくす

今回は、テストの実行は同じMac miniを想定していましたが、初回インストールなのか、どのアカウントでログインするのか、ということがテスト結果に影響するテストケースでした。そのため、「初回インストールであること」「ログインできるアカウント」を再現するために clearStateclearKeychain を実行して、アプリに紐づいていたデータを削除し、Maestroの実行コマンドにログインするためのメールアドレス、パスワードを指定するようにしました。

Parameters & Constants | Maestro by mobile.dev

メールアドレス、パスワードを他の人に見える場所に置かずに済んだので、パラメータとして与える方法を採用して良かったと思っています。

self-hosted runnersでの実行で大変だったこと

Awarefyでは他のワークフローをself-hostedで実行している実績があったので、今回のE2Eテストも同様に実行できるかと思いきや、導入にはつまづくポイントがいくつかありました。

PATHの指定

Maestroのパスを通したつもりが、なぜか通っておらず、exportコマンドをjobに組み込んでも通りませんでした。jobの中でMaestroの絶対パスを指定してコマンドを実行することにして、解決としました。

Simulator/Emulatorの起動

最初、テストするデバイスはmaestro start-device を実行することで立ち上げようと考えていました。このコマンドではOSのバージョンを指定することができますが、マイナーバージョンまでは指定することができませんでした。また、1度テストを実行した後、再度テストを実行する時にiOS Simulator、Android Emulatorどちらも立ち上がっていると、Maestroのテストコマンド実行時、デバイスを選択するところでテストが止まってしまいました。

これらの問題を解消するために、Mac mini上では常にiOS Simulator、Android Emulatorを起動しておき、Maestroのテスト実行時にデバイスのUUIDを渡すことで、iOS/Androidそれぞれでテスト実行できるようにしました。

今後検証していきたいこと

E2Eテストの実行タイミング

前述した通り、現在はテストの自動実行ができていません。ipa/apkファイルの作成、実行や、非同期処理を含むためテストケースの実行に時間がかかっており、他のワークフローの実行を止めてしまうためです。自動でテストを実行するためには、タイミングについて検討が必要です。

テスト結果の見せ方

maestro recordでの実行は2分を超えるとエラーになってしまいます。そのため、実行結果を動画で残そうとしても、一連のテストの流れにサインインが組み込まれている現状では、テスト自体が失敗してしまいます。テストを分割するなどした方が良いのか、他に方法があるのかなど、検証が必要です。

また、動画を撮影できた場合にも、GitHub Actionsの実行ログを見にいくのか、Slackに結果を流すなどできないか検証したいです。

まとめ

このようにして、アウェアファイにE2Eテストを導入できました。うまく活用していくため、テストケースを追加していくことも含めてアップデートしていきます!