アプリを作っていて、動作確認めんどいな〜と思うこと多いですよね。数行修正しただけのつもりなのにあちこち動かなくなってしまうこともあります。単体テストを書いてロジックを担保できても、動かしてみると思わぬところでバグってしまったり...
だったらE2Eテストを整備すればいいじゃないか、と思いますが、そもそも書くのも面倒だし仕様変更に追従して一つ一つ追加・修正するのも手間だなぁと感じます。
もー全部AIがやってくれよ〜
ということで、本記事はAIでE2Eテストを書いてみるテストです。
技術選定
FlutterでE2Eテストを構築できるツールはいくつかあり、好みもあるかと思いますが、以下の観点から技術選定を進めました。
- 資料が豊富か。
- インターネット上での資料が多ければ多いほど、AIの知識が多くなる・参照しやすくなる。
- 導入までの設定が少ないか。
- Flutterに限らず、ネイティブ実装に移行した場合にも転用できる。
- チーム開発において非エンジニア(QAやPdM、PLなど)も理解しやすいツール。
これらの選定基準を考慮し、今回はMaestroを使用します。
Maestroとは
Maestroは、モバイル/Web向けのUIテストをYAMLで記述できるオープンソースのE2Eフレームワークです。
YAMLで直感的な命令文で記述できるため、非エンジニアにも比較的理解しやすいツールです。
Sample
GitHub - alpha2048/maestro_e2e_sample
ベースは、RiverpodのチュートリアルのジョークAPIの実装を元にしています。
Your first Riverpod app | Riverpod
このサンプルでは、以下の2フローを用意しています。
maestro/joke_flow.yaml: ジョークの表示更新を検証maestro/notification_flow.yaml: 通知タップでアプリ復帰を検証
実装手順
Maestroのインストール
手順に従ってインストールします。Macの場合はHomebrewでもインストール可能。
※Homebrewには本ツールとは別物のMaestroというAI agentツールがcaskで提供されています。インストールを間違わないように気をつけましょう。そちらのツールも面白そうですが。
また、MaestroにはJava 17+も必要なので未インストールの場合は準備しておきましょう。
構成説明
MaestroのフローはYAMLで書きます。
最初にappIdを定義し、launchAppやtapOnなどのコマンドを並べていく構成です。
例: joke_flow.yaml
appId: com.example.maestroe2esample --- - launchApp - waitForAnimationToEnd: timeout: 5000 - copyTextFrom: id: "joke-text" - evalScript: ${output.firstJoke = maestro.copiedText} - tapOn: id: "get-another-joke"
evalScriptでJSを使えるので、比較やループも可能です。
サンプルではrepeatで最大10回リトライし、ジョークが変わったことを検証しています。
例: notification_flow.yaml
- runFlow: when: platform: ios commands: - pressKey: Home - swipe: start: 50%,1% end: 50%,55% - tapOn: "Joke"
platformでiOS/Androidの分岐が書けるので、通知や戻り操作の差異も吸収できます。
構築
AIに投げて作ります。今回はCodexに投げて作成していきます。
流れは以下の通りです。
- 画面仕様と期待動作を整理
- AIにYAMLの草案を作ってもらう
maestro testで実行し、エラーをAIに投げて修正
複雑な手順の場合は期待動作をきっちり文章に落とし込みましょう。
整理できたら、以下のように依頼していきます。
FlutterアプリのE2EをMaestroで書きたい。 「Get another joke」をタップするとジョークが変わる、ことを検証する。 実装を進めて。
あとはAIにがんがん作成してもらいます。
MaestroはSemanticsウィジェットのidentifierを識別子として使用するのですが、AIにそちらを整備してもらえます。
Semantics( identifier: 'get-another-joke', button: true, child: ElevatedButton( .. ), ),
サンプル説明
ジョーク更新フロー
ボタンを押してテキストが変わるだけのシンプルなテストです。
AIさん曰く:
copyTextFromでジョーク本文を取得し、tapOn後に再取得して変化を確認します。通信遅延を想定して、repeat + evalScript で安全側に倒しています。
アプリ外部との連携 通知で試す
若干無理矢理ではありますが、アプリ外との連携を確認するため、ローカル通知を開くことを確認しています。
通知を発火→ホームへ戻る→通知をタップして復帰、という外部連携まで検証します。
通知トレイの操作はiOS/Androidで違うため、platformで分岐しています。この辺りは使用しているシミュレータのサイズやOSによって調整が必要になりますのでご注意ください。
実行
実行方法
まずはテスト用ビルドを作成します。
iOS (Simulator用)
flutter build ios --simulator --no-codesign -t lib/main.dart
flutter build apk -t lib/main.dart
ビルドしたapk/ipaをシミュレータへインストールした上で、以下を実行します。
maestro --verbose --device <DEVICE_ID> \ test maestro/ \ --debug-output ./maestro-debug \ --test-output-dir ./maestro-artifacts
実行すると、以下のように動きます。

その他
CIに組み込むならMaestro Cloudが便利ですが、料金はそれなりに高いです。
CIへの組み込み方もいずれ別記事で検証したいところ。
おわりに
今回Maestro側の実装やコマンドの整備までAIに作成してもらいましたが、概ね狙った通りの実装ができています。
複雑なテストを実装する場合は人力で調整が必要そうですが、とにかく初手の導入に関してはAIと共に構築すればすぐにできるでしょう。
(この記事をFlutter Advent Calendar 2025辺りに載せる予定でしたが間に合いませんでした😇)











