mcpField
P
patrol

patrol

github.com/soliplexテスト・品質38 回インストール
リポジトリ

patrol について

patrol は、macOS、iOS、Android、Chrome でテストを実行する開発者向けの Flutter 用 E2E テストスキルです。デバイス指定、事前確認、アサーション手法、トラブルシューティングを案内します。

主な機能

  1. 01特定ファイルまたは全統合テストの実行
  2. 02macOS、iOS、Android、Chrome に対応
  3. 03実行前チェックリスト
  4. 04ログと条件待機によるアサーション
  5. 05一般的なテスト失敗のトラブルシューティング

活用シーン

  1. 01指定デバイスで Flutter 統合テストを実行
  2. 02テスト停止やデバイス接続の問題を調査
  3. 03アプリログで非同期イベントを検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/soliplex/flutter --skill patrol

インストール後、Claude Code で/patrolを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

setup/
android-setup.md
ios-setup.md
macos-setup.md
web-setup.md

詳細

Patrol E2E テストスキル

この Flutter プロジェクト(macOS、iOS、Android、Chrome)で Patrol 統合テストを実行・管理します。

テストの実行

CLI の場所: patrol

対話式のデバイス選択プロンプトを回避するため、必ず --device を使用してください。

macOS(デフォルト)
# Run a specific test file
patrol test \
  --device macos \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

# Run all integration tests
patrol test \
  --device macos \
  --target integration_test/ \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

初回セットアップ: エンタイトルメントとウィンドウの制約については、macOS セットアップガイドを参照してください。

iOS(シミュレーター)

起動済みシミュレーターのデバイス ID または名前を使用します。--ios フラグで OS バージョンを指定します(デフォルトは latest で、シミュレーターの OS と一致している必要があります)。

# Run on a specific iOS simulator
patrol test \
  --device <DEVICE_ID> \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

# If simulator OS != latest SDK, specify explicitly
patrol test \
  --device <DEVICE_ID> \
  --ios=18.6 \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

xcrun simctl list devices booted を使用して、デバイス ID と OS バージョンを確認してください。

初回セットアップ: RunnerUITests ターゲットとシミュレーターの OS の一致については、iOS セットアップガイドを参照してください。

Android(エミュレーターまたはデバイス)

起動済みのエミュレーター、または接続済みのデバイスが必要です。adb devices に表示される名前/シリアル番号を使用してください。

# Boot emulator (if not running)
export ANDROID_HOME=~/Library/Android/sdk
export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$PATH"
emulator -avd Patrol_Test_API_36 &

# Run on Android emulator
patrol test \
  --device emulator-5554 \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://10.0.2.2:8000

注: Android エミュレーターからホストの localhost にアクセスするには、10.0.2.2 を使用します。

初回セットアップ: AVD の作成、JDK、Google APIs イメージの要件については、Android セットアップガイドを参照してください。

Chrome(ウェブ)

Node.js >= 18 が必要です(Playwright は初回実行時に自動インストールされます)。

# Run a specific test in Chrome
patrol test \
  --device chrome \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

# Run headless (for CI or no-GUI environments)
patrol test \
  --device chrome \
  --web-headless true \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

# Custom viewport (default is browser-dependent)
patrol test \
  --device chrome \
  --web-viewport "1280x720" \
  --target integration_test/$ARGUMENTS \
  --dart-define SOLIPLEX_BACKEND_URL=http://localhost:8000

初回セットアップ: Node.js、CORS、ビューポートの詳細については、ウェブセットアップガイドを参照してください。

$ARGUMENTS が空、または "all" の場合は、integration_test/(すべてのテスト)を対象に実行します。 $ARGUMENTS が smoke_test.dart のようなファイル名の場合は、そのファイルを指定して実行します。

実行前の確認

テストを実行する前に、次の項目を確認してください。

  1. バックエンドが 上記の URL で --no-auth-mode にて実行中である
  2. patrol CLI がインストール済みである:patrol --version
  3. コードがコンパイルできる:まず dart analyze integration_test/ を実行する
  4. test_bundle.dart が最新である:Patrol が自動生成します。テストがバンドルに含まれていない場合は、ファイルを削除し、patrol test に再生成させてください

テストのパターン

pumpAndSettle は使用しない

SSE ストリームが処理の完了を妨げます。代わりに、次を使用してください。

  1. waitForCondition — UI の状態を確認するためのポーリング方式
  2. harness.waitForLog — 非同期イベント向けのストリーム方式(推奨)
  3. tester.pump(duration) — 固定時間の遅延。短い描画待ちに限って使用
標準的なテスト構造
patrolTest('description', ($) async {
  await verifyBackendOrFail(backendUrl);
  ignoreKeyboardAssertions();

  final harness = TestLogHarness();
  await harness.initialize();

  try {
    await pumpTestApp($, harness);
    // ... test body ...
  } catch (e) {
    harness.dumpLogs(last: 50);
    rethrow;
  } finally {
    harness.dispose();
  }
});
ウィジェットファインダーのリファレンス
対象ファインダー
ルームリスト項目find.byType(RoomListTile)
チャット入力欄find.byType(TextField)
送信ボタンfind.byTooltip('Send message')
チャットメッセージfind.byType(ChatMessageWidget)
設定アイコンfind.byIcon(Icons.settings)

テキスト入力には find.bySemanticsLabel を使用しないでください。TextField ではなく Semantics ラッパーが検出され、enterText が失敗することがあります。

アサーション用のログパターン
ロガーパターン意味
Routerredirect calledアプリが起動し、ルーターが有効になった
HTTP/api/v1/roomsRooms API が呼び出された
RoomRooms loaded:ルームの解析に成功した
ActiveRunRUN_STARTEDAG-UI SSE ストリームが開始された
ActiveRunTEXT_START:最初のテキストチャンクを受信した
ActiveRunRUN_FINISHEDSSE ストリームが完了した

デバッグ:2 段階のワークフロー

フェーズ 1:確認(dart MCP — flutter run)

mcp__dart-tools__launch_app を使用してアプリを起動し、実行中の ウィジェットツリーとログを調べて、テストに適したファインダーとログパターンを見つけます。

mcp__dart-tools__launch_app   → starts app, returns DTD URI
mcp__dart-tools__connect_dart_tooling_daemon → connect to running app
mcp__dart-tools__get_widget_tree(summaryOnly: true) → see real widget hierarchy
mcp__dart-tools__get_app_logs(pid, maxLines: 50)    → see stdout log output
mcp__dart-tools__get_runtime_errors                  → check for exceptions

標準出力のログは [DEBUG] Router: redirect called for / のように表示されます。これらを使って、harness.expectLog() 用のロガー名とメッセージパターンを特定してください。

ウィジェットツリーには実際の widgetRuntimeType の値が表示されます。これらを find.byType() ファインダーに使用してください。

フェーズ 2:検証(TestLogHarness — patrol の実行)

Patrol は xcodebuild を通じてアプリを起動しますが、DTD URI は公開しません。そのため、patrol テストの実行中は dart MCP ツールに接続できません。

代わりに、すべてのテストアサーションでは TestLogHarness を使用し、MemorySink を介してプロセス内でログをキャプチャします。

  • harness.expectLog('Router', 'redirect called') — 同期チェック
  • harness.waitForLog('ActiveRun', 'RUN_FINISHED') — ストリームベースの待機
  • harness.dumpLogs(last: 50) — 失敗時にコンソールへログを出力

ハーネスは get_app_logs に表示されるものと同じ [DEBUG] ログを確認できますが、stdout 経由ではなくプロセス内からアクセスします。

Git Worktree に関する注意点

このリポジトリは git worktree です。flutter/dart を実行する pre-commit フックでは、まず unset GIT_DIR を実行する必要があります。そうしないと、Flutter はバージョンを 0.0.0-unknown と報告します。scripts/ 内のラッパースクリプトがこの処理を行います。

トラブルシューティング

症状原因対処方法
「複数のデバイスが見つかりました」というプロンプト--device フラグがない--device macos を追加します
command not found: patrolPATH に ~/.pub-cache/bin がないPATH に追加するか、フルパスを使用します
「Waiting for app」でテストが停止するEntitlements がない、または誤っているmacOS のセットアップを参照します
バージョンが 0.0.0-unknown と表示されるworktree で GIT_DIR が設定されているscripts/flutter-analyze.sh ラッパーを使用します
「10 秒以内に表示されませんでした」ウィジェットが body ではなく drawer にあるmacOS のセットアップを参照します
enterText で Bad state: No element が発生するFinder が TextField ではなく Semantics に一致した代わりに find.byType(TextField) を使用します
アクセシビリティ権限が拒否されるEntitlements が変更された「システム設定」>「プライバシーとセキュリティ」>「アクセシビリティ」で再承認します
iOS:xcodebuild の終了コードが 70OS=latest がシミュレーターに一致しないiOS のセットアップを参照します
iOS:「デバイス ... が接続されていません」デバイス ID の形式が間違っているxcrun simctl list devices booted の UUID を使用します
Android:「接続されたデバイスがありません」エミュレーターが起動していない、または PATH に adb がないAndroid のセットアップを参照します
Android:localhost への接続が拒否されるエミュレーターからホストの localhost に接続できないlocalhost の代わりに 10.0.2.2 を使用します
Android:起動時に Gmail のログインプロンプトが表示されるAVD が Google Play イメージを使用しているAndroid のセットアップを参照します
Chrome:「playwright モジュールが見つかりません」Node.js がインストールされていないWeb のセットアップを参照します
Chrome:テストで CORS エラーが発生するバックエンドに CORS ヘッダーがないWeb のセットアップを参照します
Chrome:verifyBackendOrFail で「Failed to fetch」が発生するバックエンドがオフライン、または CORS によってブロックされているバックエンドを起動し、ブラウザーのコンソールを確認します

コメント