flutter-mobile-automation
FlutterアプリのE2Eテストとモバイル自動化の知見。Maestro、Mobile MCP、Dart MCPを使用したテスト作成・実行時に使用。
$ 설치
git clone https://github.com/K9i-0/flutter_e2e_investigation /tmp/flutter_e2e_investigation && cp -r /tmp/flutter_e2e_investigation/.claude/skills/flutter-mobile-automation ~/.claude/skills/flutter_e2e_investigation// tip: Run this command in your terminal to install the skill
name: flutter-mobile-automation description: FlutterアプリのE2Eテストとモバイル自動化の知見。Maestro、Mobile MCP、Dart MCPを使用したテスト作成・実行時に使用。
Flutter Mobile Automation
E2Eテスト自動化と開発ワークフロー自動化の両方をカバー。
推奨構成: Maestro MCP + Dart MCP
FlutterアプリのE2Eテストと開発自動化には Maestro MCP + Dart MCP の組み合わせが最適。
Dart MCP vs CLI 使い分け
原則: DTD接続が必要な操作はMCP、それ以外はCLI
MCP推奨(CLIで代替不可)
| 操作 | ツール | 理由 |
|---|---|---|
| アプリ起動 | launch_app | DTD URI取得が必要 |
| DTD接続 | connect_dart_tooling_daemon | MCP専用 |
| ホットリロード | hot_reload | DTD経由で実行 |
| ホットリスタート | hot_restart | DTD経由で実行 |
| ウィジェットツリー | get_widget_tree | DTD経由でのみ取得可能 |
| ランタイムエラー | get_runtime_errors | DTD経由でのみ取得可能 |
| アプリログ | get_app_logs | 起動中アプリのログ取得 |
CLI推奨(MCPより効率的)
| 操作 | CLI コマンド | 理由 |
|---|---|---|
| デバイス一覧 | flutter devices | 出力がシンプル |
| テスト実行 | flutter test | 出力が見やすい、オプション豊富 |
| 静的解析 | dart analyze | 出力が見やすい |
| フォーマット | dart format . | 高速、差分表示 |
| 依存関係追加 | flutter pub add <pkg> | インタラクティブ |
| 依存関係取得 | flutter pub get | 高速 |
| プロジェクト作成 | flutter create | オプション豊富 |
| ビルド | flutter build ios/apk | 詳細な出力 |
典型的なワークフロー
# 1. CLI: ビルド・インストール
flutter build ios --simulator
xcrun simctl install booted build/ios/iphonesimulator/Runner.app
# 2. MCP: アプリ起動・DTD接続
# → launch_app → connect_dart_tooling_daemon
# 3. MCP: 開発中のデバッグ
# → get_widget_tree, get_runtime_errors, hot_reload
# 4. CLI: テスト・解析
flutter test
dart analyze
ツール概要
Maestro / Maestro MCP
- Flutter第一級サポートのE2Eテストフレームワーク
- CLI: YAMLでテストを記述 (
maestro test .maestro/test.yaml) - MCP: Claude Codeから対話的に操作(YAML不要)
- スクリーンショット取得
- タップ操作(テキスト/id指定、座標計算不要)
- view hierarchy取得
Dart MCP Server
- Flutter開発連携用MCPサーバー
- ウィジェットツリー取得(Flutter内部構造)
- ホットリロード/リスタート
- ランタイムエラー取得
Mobile MCP(参考)
- Maestro MCPで代替可能なため通常は不要
- 座標ベースの操作が必要な特殊ケースでのみ使用
Claude Code セットアップ
推奨設定(.claude/settings.json)
プロジェクト共通の許可設定はリポジトリに含めて共有:
{
"permissions": {
"allow": [
"Bash(flutter:*)",
"Bash(dart:*)",
"Bash(adb:*)",
"Bash(xcrun simctl:*)",
"Bash(maestro:*)",
"Bash(MAESTRO_DRIVER_STARTUP_TIMEOUT=* maestro:*)",
"Bash(git:*)",
"Bash(gh:*)",
"WebSearch",
"WebFetch(domain:github.com)",
"WebFetch(domain:docs.maestro.dev)",
"mcp__dart-mcp__*",
"mcp__maestro__*"
]
}
}
ローカル設定(.claude/settings.local.json)
ユーザー固有の設定は.gitignoreに追加してローカルのみ:
{
"permissions": {
"additionalDirectories": [
"/Users/<user>/.maestro/tests"
]
}
}
Maestroテスト出力ディレクトリ
Maestro CLIはテスト結果を~/.maestro/tests/に出力する。
Claude Codeでエラーログを確認するには、このディレクトリを追加:
# Claude Codeで以下を実行
/add-dir ~/.maestro/tests
または.claude/settings.local.jsonに追加:
{
"permissions": {
"additionalDirectories": [
"/Users/<user>/.maestro/tests"
]
}
}
Maestro CLI プラットフォーム選択
推奨: iOSシミュレーター
Maestro CLIはiOSシミュレーターでの実行を推奨。
| プラットフォーム | launchApp | 安定性 | 備考 |
|---|---|---|---|
| iOS 18.x | ✅ | ⭐⭐⭐ | 推奨 |
| Android API 30 | ⚠️ | ⭐ | 初回のみ動作、以降不安定 |
| Android API 34+ | ❌ | - | TCP forwarding / timeout エラー |
既知の問題(Maestro 2.0.10)
- Issue #2839: launchAppが最初の1回しか動作しない
- Issue #1927: Maestro Studio起動中はlaunchApp失敗
iOS E2Eテスト実行
# iOSシミュレーター起動
xcrun simctl boot "iPhone 16 Pro"
# iOSアプリビルド・インストール
flutter build ios --simulator
xcrun simctl install booted build/ios/iphonesimulator/Runner.app
# テスト実行(タイムアウト増加推奨)
MAESTRO_DRIVER_STARTUP_TIMEOUT=120000 \
maestro --device "<DEVICE_ID>" \
test -e APP_ID=com.example.flutterE2eInvestigation \
.maestro/test.yaml
Bundle ID の違い
iOS/Androidでbundle IDが異なる場合、環境変数で切り替え:
# .maestro/test.yaml
appId: ${APP_ID}
---
- launchApp:
clearState: true
# iOS
maestro test -e APP_ID=com.example.flutterE2eInvestigation .maestro/test.yaml
# Android
maestro test -e APP_ID=com.example.flutter_e2e_investigation .maestro/test.yaml
Flutterビルドモードの使い分け
ビルドモード比較
| モード | 用途 | ホットリロード | パフォーマンス |
|---|---|---|---|
| debug | 機能開発・iOSテスト | ✅ | 低速 |
| profile | パフォーマンス計測 | ❌ | 高速 |
| release | 本番 | ❌ | 最速 |
ビルドコマンド
# 機能開発用・iOSテスト(debug)
flutter run
flutter build ios --simulator
# パフォーマンス計測(profile)- Androidのみ
flutter build apk --profile
# 本番用(release)
flutter build apk --release
flutter build ios --release
ワークフロー推奨
開発中:
debug ビルド → Dart MCP で起動 → hot reload で変更反映
E2Eテスト:
iOS シミュレーター → debug ビルド → Maestro CLI でテスト実行
Maestro + Flutter ベストプラクティス
Semanticsウィジェットの活用
Maestroは要素をセマンティクス情報で認識する。FlutterではSemanticsウィジェットでアクセシビリティ情報を提供:
// ラベルで識別可能にする
Semantics(
label: 'submit-button',
child: ElevatedButton(
onPressed: _submit,
child: Text('Submit'),
),
)
// identifier属性を使用(Flutter 3.19+)
Semantics(
identifier: 'login-button',
child: ElevatedButton(...),
)
Maestroでの要素指定
# tooltipテキストで認識(FloatingActionButtonなど)
- tapOn: "Increment"
# テキストで認識
- tapOn: "Submit"
# 正規表現
- tapOn:
text: ".*Login.*"
# identifier使用時(推奨)
- tapOn:
id: "login-button"
テストファイル構造
.maestro/
├── login_test.yaml
├── cart_test.yaml
└── checkout_flow.yaml
基本的なテスト例
appId: com.example.myapp
---
- launchApp
- assertVisible: "Welcome"
- tapOn: "Login"
- inputText:
id: "email-field"
text: "test@example.com"
- tapOn: "Submit"
- assertVisible: "Dashboard"
トラブルシューティング
要素が見つからない場合
maestro hierarchyでUI階層を確認- Semanticsウィジェットでラベル/識別子を追加
- テキストマッチングを正規表現に変更
Flutterウィジェットが認識されない場合
SemanticsウィジェットでラップexcludeSemantics: falseを確認MergeSemanticsで子要素を統合
ツール使い分けガイド
機能比較表
| 機能 | Dart MCP | Maestro MCP |
|---|---|---|
| ウィジェットツリー | ✅ 詳細(Flutter内部) | - |
| アクセシビリティ情報 | - | ✅ accessibilityText |
| スクリーンショット | - | ✅ |
| 要素一覧 | - | ✅ CSV形式 |
| タップ操作 | ⚠️ 要設定 | ✅ テキスト/id指定 |
| ホットリロード | ✅ | - |
| ランタイムエラー | ✅ | - |
| E2Eシナリオ記述 | - | ✅ YAML / 対話的 |
推奨される使い分け
Maestro MCP (UI操作・確認)
- スクリーンショット取得
- タップ操作(テキスト指定、座標計算不要)
- view hierarchy確認
- E2Eテストシナリオ作成・実行
Dart MCP (開発・デバッグ)
- ウィジェットツリーの詳細確認
- ランタイムエラーの取得
- ホットリロード/リスタート
- アプリ起動とDTD接続
典型的なワークフロー
1. Dart MCP: アプリ起動 → DTD接続
2. Dart MCP: ウィジェットツリーでUI構造確認
3. Maestro MCP: スクリーンショットで視覚確認
4. Maestro MCP: 対話的にタップ操作
5. Maestro CLI: E2Eテストシナリオ作成・実行
6. Dart MCP: エラー時はランタイムエラー確認
Dart MCP flutter_driver使用時
アプリ側に設定が必要:
// lib/driver_main.dart
import 'package:flutter_driver/driver_extension.dart';
import 'main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
実践知見(TODOアプリ検証)
Semantics identifier → Android resource-id
Semantics.identifierはAndroidでresource-idとして認識される:
Semantics(
identifier: 'todo-fab-add', // → resource-id=todo-fab-add
label: 'Add new todo', // → accessibilityText
child: FloatingActionButton(...),
)
view hierarchy出力例:
accessibilityText=Add new todo; resource-id=todo-fab-add; class=android.widget.Button
動的IDパターン
リスト要素には動的IDを使用:
Semantics(
identifier: 'todo-tile-${todo.id}', // 例: todo-tile-5cd04af0-9019...
identifier: 'todo-checkbox-${todo.id}', // 例: todo-checkbox-5cd04af0-...
...
)
Maestroでの指定方法:
# 動的IDはテキストマッチングで対応
- tapOn:
text: "Mark Buy groceries as done" # accessibilityTextで指定
Semantics命名規則(実証済み)
パターン: {機能}-{要素タイプ}[-{動的ID}]
静的要素:
- todo-fab-add # FAB
- search-field # 検索フィールド
- save-button # 保存ボタン
- category-chip-work # カテゴリチップ
動的要素:
- todo-tile-{uuid} # Todoタイル
- todo-checkbox-{uuid} # チェックボックス
Maestro MCPの制限事項
-
日本語入力非対応:
inputTextでUnicodeエラーFailed to input text: Unicode not supported: 買い物に行く→ 英語でテストするか、Maestro CLIを使用
-
動的ID指定:
id:では動的UUIDを直接指定できない →text:(accessibilityText)で代替 -
スクロール操作:
run_flowでスワイプを使用appId: any --- - swipe: start: 50%, 80% end: 50%, 30%
動画録画
E2Eテストの視覚的検証用に動画を録画する方法:
xcrun simctl(推奨)
# 録画開始(バックグラウンド)
xcrun simctl io <device_id> recordVideo --codec=h264 /path/to/video.mp4 &
# 録画停止
kill <pid> # または Ctrl+C
Maestro MCP
# フロー内で録画
- startRecording: recording_name
- tapOn: "Button"
- stopRecording
# → ~/.maestro/tests/ に保存
| 方法 | 利点 | 欠点 |
|---|---|---|
| xcrun simctl | 保存先自由、軽量 | 別プロセス管理が必要 |
| Maestro | フロー内完結 | 保存先固定 |
エミュレーターへの画像追加
ギャラリー画像テスト用:
# 画像をエミュレーターにプッシュ
adb -s emulator-5554 push /path/to/image.png /sdcard/Pictures/
# メディアスキャンでギャラリーに認識させる
adb -s emulator-5554 shell am broadcast \
-a android.intent.action.MEDIA_SCANNER_SCAN_FILE \
-d file:///sdcard/Pictures/image.png
Riverpod + SharedPreferences構成
検証済みの構成:
lib/
├── main.dart # ProviderScope設定
├── app.dart # MaterialApp
└── features/todo/
├── data/
│ ├── models/ # Todo, Category
│ └── repositories/ # SharedPreferences操作
├── providers/ # AsyncNotifierProvider
└── ui/
├── screens/ # 画面
└── widgets/ # Semantics付きウィジェット
動作確認済みワークフロー
1. Dart MCP: launch_app → connect_dart_tooling_daemon
2. Dart MCP: get_widget_tree でSemantics配置確認
3. Maestro MCP: inspect_view_hierarchy で状態確認(軽量)
4. Maestro MCP: tap_on (id/text指定) でUI操作
5. Maestro MCP: take_screenshot で視覚確認(必要時のみ)
6. Maestro CLI: YAMLテスト作成・自動実行
効率的な状態確認パターン
推奨: アクセシビリティ情報優先
画面の状態確認は以下の順序で行う:
-
inspect_view_hierarchy(第一優先)
- 軽量で高速
- resource-id、accessibilityText、bounds情報を取得
- 操作対象の要素特定に十分
-
take_screenshot(必要時のみ)
- 視覚的確認が必要な場合のみ使用
- レイアウト崩れ、色、画像の確認
- ユーザーへの結果報告
❌ 非効率: screenshot → 確認 → 操作 → screenshot → ...
✅ 効率的: hierarchy → 操作 → hierarchy → ... → screenshot(最終確認)
view hierarchy出力例:
element_num,bounds,attributes
54,"[53,1788][1028,2313]","accessibilityText=Attached image; resource-id=image-attachment-field; class=android.widget.ImageView"
67,"[53,2201][1028,2348]","accessibilityText=Save todo; resource-id=save-button; class=android.view.View"
この情報だけで要素の存在確認と操作が可能。
開発フロー
機能実装の流れ
1. 計画(Plan Mode)
- EnterPlanMode で設計開始
- 不明点は AskUserQuestion で確認
- 計画ファイルに実装詳細を記述
- ExitPlanMode で承認を得る
2. 実装
- TodoWrite でタスク分解・進捗管理
- 1タスク完了ごとに completed に更新
- コード変更は最小限に
3. E2Eテスト
- Dart MCP: アプリ起動・DTD接続
- Maestro MCP: inspect_view_hierarchy で状態確認(軽量)
- Maestro MCP: tap_on / input_text で操作
- Maestro MCP: take_screenshot で視覚確認(必要時のみ)
4. コミット・プッシュ
- Conventional Commits 形式
- 機能単位でコミット
- 適切なタイミングでプッシュ
E2Eテスト時の状態確認パターン
✅ 推奨フロー:
hierarchy → 操作 → hierarchy → 操作 → ... → screenshot(最終確認)
❌ 非効率フロー:
screenshot → 操作 → screenshot → 操作 → ...
Git運用
# コミット
git add -A && git commit -m "type(scope): description"
# プッシュ
git push
# リモート設定(初回のみ)
git remote add origin git@github.com:USER/REPO.git
git push -u origin main
参考リンク
Repository
