maestro-mobile-testing について
Maestro Mobile E2E Testingは、モバイルアプリのE2Eテストを宣言型YAMLで作成するためのスキルです。再利用可能なフロー、セレクター、認証パターン、スクリプト作成を案内し、さまざまなUI状態の検証に役立ちます。
主な機能
- 01宣言型YAMLテストフロー
- 02自動待機と組み込みリトライ
- 03再利用可能なサブフローとセレクター
- 04GraalJSによるJavaScriptスクリプト
- 05認証とUI状態テストのガイド
活用シーン
- 01再現可能なモバイルスモークテストの作成
- 02ログインとOTPフローのテスト
- 03認証状態ごとのUI動作の検証
- 04モックサーバーを使ったAPI依存画面のテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tovimx/ maestro-mobile-testing-skill --skill maestro-mobile-testingインストール後、Claude Code で/maestro-mobile-testingを入力して読み込まれたか確認します。
詳細
Maestro モバイル E2E テスト
概要
Maestro は、宣言型の YAML ベースのモバイル E2E テストフレームワークです。自動待機、組み込みのリトライロジック、そしてボイラープレートなしの高速実行を提供します。React Native アプリでは、Detox や Appium よりも安定しています。
主な機能
- 宣言型 YAML — 命令型のテストコードは不要、ステップを書くだけ
- 自動待機 — 手動の
sleep()や不安定な待機は不要 - 組み込みのリトライ — テストの不安定性を軽減
- 高速実行 — セットアップのオーバーヘッドなしで素早く実行
- Maestro Studio — インタラクティブなテストビルダー(
maestro studio) - サブフロー — DRY なテストのための再利用可能な YAML シーケンス
- JavaScript スクリプティング — HTTP 呼び出しやデータ操作のための GraalJS ランタイム
- Maestro Cloud — ローカルシミュレーターなしで CI 上で実機テスト
クイックスタート
インストール
curl -Ls "https://get.maestro.mobile.dev" | bash
brew install openjdk@17
export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
最小限のテスト
appId: com.myapp
---
- launchApp
- tapOn:
id: "my-button"
- assertVisible: "Expected Text"
実行
maestro test .maestro/smoke-test.yaml
maestro test --debug .maestro/smoke-test.yaml # step through
maestro studio # interactive builder
コアパターン
1. セレクター戦略: testID とテキストの比較
セレクターのアプローチはプロジェクトの状況に応じて選択してください。どちらも有効であり、適切な選択はアプリが多言語対応かどうか、そしてチームのテスト方針によって決まります。
| 状況 | 推奨されるセレクター | 理由 |
|---|---|---|
| 多言語 / i18n | id:(testID) | 翻訳に対して安定 |
| 単一言語 | テキストラベル | 人間が読みやすく、自己文書化されたテスト |
| エージェントが保守するテスト | どちらでも — 開発者に確認 | AI が保守するフローでは可読性の重要度は低い |
| システムダイアログ | テキスト(常に) | ネイティブのアラートでは testID は使用不可 |
# testID selector — stable across translations
- tapOn:
id: "submit-button"
# Text selector — human-readable, self-documenting
- tapOn: "Submit"
testID を優先すべき場合:
- アプリが複数の言語をサポートしている、または翻訳される予定がある
- UI のテキストが動的、または頻繁に変更される
- 複数の要素が同じ表示テキストを共有している
テキストセレクターを優先すべき場合:
- 文言が安定した単一言語のアプリ
- 可読性と自己文書化が優先される
- ユーザーに表示される動作を、表示されているとおりに正確にテストする
React Native では、ID ベースのセレクターを使用する場合に testID プロパティを追加します。
<TouchableOpacity testID="submit-button" onPress={handleSubmit}>
<Text>{t('submit')}</Text>
</TouchableOpacity>
testID の命名規則
ID ベースのセレクターを使用する場合:
{component}-{action/type}[-{variant}]
Examples:
- auth-prompt-login-button
- product-card-{id}
- otp-input-0
- tab-home
- dashboard-loading
2. 認証プリフライトパターン
Maestro が認証状態の解決前に UI を操作してしまう競合状態を防ぎます。認証の読み込みが完了したときにのみレンダリングされる、サイズゼロの auth-loaded マーカーを追加します。
// In your tab bar or root layout
{!isLoading && <View testID="auth-loaded" style={{ width: 0, height: 0 }} />}
そして、すべてのテストで:
- launchApp
# Prevent XCTest crash on cold boot (iOS)
- swipe:
direction: DOWN
duration: 100
# Wait for auth state to resolve
- extendedWaitUntil:
visible:
id: "auth-loaded"
timeout: 15000
# Now safe to interact
- tapOn:
id: "tab-home"
3. 適応型テスト(両方の認証状態を処理)
テストは、ユーザーが認証済みかどうかに関係なく動作する必要があります。
# Auth flow — only runs if login prompt is visible
- runFlow:
when:
visible: "Sign In"
file: flows/auth-flow.yaml
# Already authenticated — proceed directly
- runFlow:
when:
visible:
id: "tab-home"
file: flows/authenticated-action.yaml
4. 楽観的更新のテスト
短いタイムアウトを使用して、サーバーの応答より前に UI の変更が発生することを検証します。
# Trigger mutation
- tapOn:
id: "action-button"
# OPTIMISTIC: UI must change within 3s (not waiting for server)
- extendedWaitUntil:
visible:
id: "undo-button"
timeout: 3000
# Verify derived UI state
- extendedWaitUntil:
visible:
id: "user-indicator"
timeout: 5000
| アクション | 期待される変化 | タイムアウト |
|---|---|---|
| ミューテーションのトリガー | ボタンの状態が切り替わる | < 3s |
| リストの更新 | 項目が表示/非表示になる | < 5s |
| アクションの再実行 | 永続性を証明 | < 3s |
5. ネイティブアラートの閉じ方
React Native の Alert.alert() は、UI をブロックするネイティブダイアログを生成します。
- tapOn:
id: "action-button"
# Wait for expected state change first
- extendedWaitUntil:
visible:
id: "new-state-element"
timeout: 5000
# Dismiss alert (optional in case it already closed)
- tapOn:
text: "OK"
optional: true
# Brief delay for alert animation
- swipe:
direction: DOWN
duration: 300
6. 再利用性のためのサブフロー
繰り返されるシーケンスをサブフローファイルに分割します。
.maestro/
├── flows/
│ ├── auth-and-return.yaml
│ ├── complete-purchase.yaml
│ └── verify-result.yaml
├── smoke-test.yaml
└── feature-test.yaml
# In main test
- runFlow:
file: flows/auth-and-return.yaml
7. ディープリンク(Expo)
バンドル ID ではなく、app.json の Expo スキームを使用します。
# WRONG
- openLink: "com.myapp://profile/settings"
# CORRECT
- openLink: "myapp://profile/settings"
ディープリンクは、アプリのディープリンクハンドラーに登録されている必要があります。登録されていないルートはサイレントに失敗します。
8. プラットフォーム固有のロジック
- runFlow:
when:
platform: ios
file: flows/ios-specific.yaml
- runFlow:
when:
platform: android
file: flows/android-specific.yaml
9. 環境変数
appId: com.myapp
env:
TEST_EMAIL: [email protected]
API_BASE_URL: http://localhost:3000
---
- inputText: ${TEST_EMAIL}
10. セレクターの状態プロパティ
enabled、selected、checked、focused を使用して、要素を現在の状態でターゲットにします。これは、アクションの前後にインタラクティブな要素の状態を検証するのに便利です。
# Only tap the submit button if it's enabled
- tapOn:
id: "submit-button"
enabled: true
# Assert a checkbox is checked
- assertVisible:
id: "terms-checkbox"
checked: true
# Wait for an input to be focused
- extendedWaitUntil:
visible:
id: "email-input"
focused: true
timeout: 3000
| プロパティ | 値 | ユースケース |
|---|---|---|
enabled | true / false | 送信中、またはフォームが有効になるまで無効化されるボタン |
checked | true / false | チェックボックス、トグルスイッチ |
selected | true / false | タブ項目、セグメント化コントロール |
focused | true / false | 自動フォーカスされる入力フィールド |
11. 相対位置セレクター
類似した要素を、他の要素との空間的な関係によって区別します。これはインデックスベースの選択よりも慣用的で、耐性があります。
# BAD — fragile, breaks if order changes
- tapOn:
text: "Add to Basket"
index: 1
# GOOD — contextual, self-documenting
- tapOn:
text: "Add to Basket"
below:
text: "Awesome Shoes"
利用可能な相対セレクター:
# Target element below another
- tapOn:
text: "Buy Now"
below: "Product Title"
# Target element that is a child of a parent
- tapOn:
text: "Delete"
childOf:
id: "item-card-42"
# Target a parent that contains a specific child
- tapOn:
containsChild: "Urgent"
# Target by multiple descendants
- tapOn:
containsDescendants:
- id: title_id
text: "Specific Title"
- "Another descendant text"
# Horizontal positioning
- tapOn:
text: "Edit"
rightOf: "Username"
| セレクター | 意味 |
|---|---|
below: | 要素が参照要素の下に位置する |
above: | 要素が参照要素の上に位置する |
leftOf: | 要素が参照要素の左にある |
rightOf: | 要素が参照要素の右にある |
childOf: | 要素が参照親の直接の子である |
containsChild: | 要素が参照に一致する直接の子を含む |
containsDescendants: | 要素が指定されたすべての子孫要素を含む |
認証テスト
アーキテクチャ
E2E で OTP またはマジックリンク認証をテストするには、プログラムによってメールを取得する必要があります。一般的なパターン:
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
│ Maestro │────▶│ Auth │────▶│ Email Capture │
│ Test │ │ Provider │ │ Service │
└─────────────┘ └──────────────┘ └─────────────────┘
│ │
│ ┌──────────────────────────────┘
│ ▼
│ ┌─────────────┐
└──▶│ REST API │ ─── GET /api/v1/messages
│ (email) │ ─── Extract OTP code
└─────────────┘
一般的なメール取得サービス: Mailpit、MailHog、Ethereal。
OTP 取得スクリプト
Maestro の GraalJS ランタイムを使用して、メール取得サービスから OTP コードを取得します:
// CRITICAL: Maestro uses GraalJS — NO async/await, NO fetch()
var email = typeof EMAIL !== "undefined" ? EMAIL : "[email protected]";
var emailServiceUrl = typeof EMAIL_SERVICE_URL !== "undefined"
? EMAIL_SERVICE_URL : "http://localhost:8025";
var response = http.get(emailServiceUrl + "/api/v1/messages");
if (!response.ok) {
throw new Error("Failed to fetch emails: " + response.status);
}
var data = json(response.body);
// Find the latest email and extract OTP code
var body = data.messages[0].Content.Body;
var match = body.match(/(\d{6})/);
output.OTP_CODE = match[1];
OTP 入力戦略
自動フォーカスする OTP コンポーネントでは、各桁を個別に入力する必要があります。入力する前に各入力欄をタップします:
# Split OTP into digits via helper script
- runScript:
file: scripts/split-otp.js
env:
OTP_CODE: ${output.OTP_CODE}
# Enter each digit by tapping its input
- tapOn:
id: "otp-input-0"
- inputText: ${output.OTP_0}
- tapOn:
id: "otp-input-1"
- inputText: ${output.OTP_1}
# ... repeat for all digits
プロバイダー固有の実装 (Supabase + Mailpit、Firebase Auth、Auth0) については、これを拡張するプロジェクトレベルのスキルを作成してください。
GraalJS スクリプトのルール
Maestro は GraalJS ランタイムを使用します。これらの制約は絶対です:
| 機能 | 状態 |
|---|---|
async/await | サポートされていません |
fetch() | サポートされていません |
http.get()、http.post() | 代わりにこれらを使用してください |
json() | レスポンスボディの解析に使用します |
output.VAR | YAML フローで使用する変数を設定します |
var 宣言 | 必須 (安全性のため const/let ではなく var を使用してください) |
// Script template
var response = http.get("http://localhost:8025/api/endpoint");
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
var data = json(response.body);
output.RESULT = data.value;
重要な落とし穴
clearState は iOS キーチェーンをクリアしません
clearState: true はアプリのサンドボックス (UserDefaults、ファイル、キャッシュ) をクリアしますが、iOS キーチェーンはクリアしません。expo-secure-store (またはキーチェーンベースのストレージ) 経由で保存された認証トークンは、clearState リセット後も、さらにはアプリの再インストール後も保持されます。
# WRONG — user may still be authenticated
- launchApp:
clearState: true
- assertVisible: "Welcome" # Fails if Keychain has tokens
# CORRECT — wait for auth resolution, then adapt
- launchApp
- extendedWaitUntil:
visible:
id: "auth-loaded"
timeout: 15000
ルール:
- iOS でゲスト状態を生成するために
clearStateに依存しないでください - 認証テストの場合:
clearStateをスキップし、auth-loadedプレフライトを使用してください - ゲストテストの場合: 両方の状態を処理するアダプティブフローを使用してください
clearStateの後にゲスト専用 UI をアサートしないでください
注意: Android では、clearState: true は認証情報を含むアプリデータを完全にリセットします。これは iOS のみの落とし穴です。
XCTest kAXErrorInvalidUIElement クラッシュ (iOS)
コールドブートで最初のレンダリングサイクルが完了する前に Maestro がアクセシビリティツリーと相互作用すると、XCTest ドライバーがクラッシュする可能性があります。
修正: launchApp の直後に no-op スワイプを追加します:
- launchApp
- swipe:
direction: DOWN
duration: 100
API サーバーへの依存
localhost のバックエンド API を呼び出すモバイルアプリには、完全なサーバーまたはモックサーバーが実行されている必要があります。これがないと、API に依存するすべての画面にローディングスピナーまたは空の状態が表示されます (クエリはサイレントに失敗します)。
修正: Maestro テストを実行する前にモック API サーバーを起動します:
# Start mock server (serves canned responses on your API port)
npx tsx scripts/mock-api-server.ts &
# Then run tests
maestro test .maestro/my-test.yaml
アプリが呼び出す各エンドポイントに対して定型の JSON を返す軽量なモックを作成してください。これは完全なバックエンドを実行するよりも高速で、より決定論的です。
認証対応タブバー
ゲストと認証済みユーザーで異なるタブを表示するタブバーは、セレクターの失敗を引き起こします:
| 状態 | 一般的なタブ |
|---|---|
| ゲスト | home、search、cart、profile |
| 認証済み | home、feed、create、messages、profile |
両方の状態に存在するタブのみをアサートするか、アダプティブな when: 条件を使用してください。
テストファイルテンプレート
# {Feature} {Action} Test
#
# Tests: {what this validates}
# Prerequisites:
# - Simulator/emulator running with app installed
# - Backend or mock server running (if API-dependent)
appId: com.myapp
env:
TEST_EMAIL: maestro-{feature}@example.com
EMAIL_SERVICE_URL: http://localhost:8025
---
# ==========================================
# STEP 1: LAUNCH + AUTH PRE-FLIGHT
# ==========================================
- launchApp
- swipe:
direction: DOWN
duration: 100
- extendedWaitUntil:
visible:
id: "auth-loaded"
timeout: 15000
- takeScreenshot: 01-initial-state
# ==========================================
# STEP 2: {ACTION}
# ==========================================
- tapOn:
id: "target-element"
# ==========================================
# STEP 3: VERIFY
# ==========================================
- extendedWaitUntil:
visible:
id: "expected-result"
timeout: 5000
- takeScreenshot: 02-final-state
フォルダ構成
.maestro/
├── README.md # Quick reference + testID inventory
├── config.yaml # Shared configuration
├── flows/ # Reusable sub-flows
│ ├── auth-and-return.yaml
│ ├── complete-action.yaml
│ └── verify-result.yaml
├── scripts/ # GraalJS helpers
│ ├── fetch-otp.js
│ └── split-otp.js
├── smoke-test.yaml # Guest navigation
├── auth-signin.yaml # OTP sign-in flow
├── feature-screenshots.yaml # Screenshot capture flows
└── feature-action.yaml # Feature-specific tests
scripts/
├── mock-api-server.ts # Lightweight mock for E2E
└── run-e2e.sh # Orchestration script
命名規則
| 種類 | パターン | 例 |
|---|---|---|
| メインテスト | {feature}-{action}.yaml | checkout-purchase.yaml |
| サブフロー | {action}-{context}.yaml | auth-and-return-to-dashboard.yaml |
| スクリプト | {verb}-{noun}.js | fetch-otp.js |
インフラストラクチャ
API 依存テスト用のモックサーバー
API レイヤーに対して定型レスポンスを返す軽量なモックサーバーを作成します。これは、E2E テスト中に完全なバックエンドを実行するよりも高速で、より確実です。
# Start mock server before running Maestro tests
npx tsx scripts/mock-api-server.ts &
オーケストレーションスクリプト
シェルスクリプトで E2E のセットアップ全体を自動化します。スクリプトは以下を行います。
- バックエンドサービス(データベース、認証)を起動する
- テストデータを投入する
- モック API サーバーを起動する
- Maestro テストを実行する
- すべてのサービスをクリーンアップする
bash scripts/run-e2e.sh
シードデータ
特定のデータに依存するテストには、シード済みのデータベースが必要です。シードスクリプトはテストインフラストラクチャのそばに置き、各テストスイートの前に実行してください。
Android 固有のパターン
エミュレーターのセットアップ
Android のテストには、エミュレーターまたは USB 接続された実機が必要です。Maestro は接続されたデバイスを自動検出します。
# List available system images
sdkmanager --list | grep system-images
# Create emulator
avdmanager create avd -n maestro_test \
-k "system-images;android-34;google_apis;arm64-v8a"
# Start emulator
emulator -avd maestro_test
iOS と Android の違い
| 項目 | iOS | Android |
|---|---|---|
| デバイスの種類 | シミュレーターのみ(実機不可) | エミュレーター + ADB 経由の実機 |
clearState | Keychain をクリアしません | アプリデータを完全にリセットします |
| コールドブート時のクラッシュ | XCTest kAXError(スワイプ遅延を追加) | 同等の問題はありません |
| パフォーマンス | ネイティブで動作(高速) | ARM エミュレーション(x86 では低速) |
| 権限ダイアログ | システムアラート | テキストが異なるシステムダイアログ |
ADB によるデバッグ
adb devices # List connected devices
adb shell am start -n com.myapp/.MainActivity # Launch app
adb logcat | grep Maestro # Filter Maestro logs
adb shell input keyevent 82 # Unlock screen
Android の権限処理
Android の権限はシステムダイアログとして表示されます。オプションのタップで閉じます。
- tapOn:
text: "Allow"
optional: true
- tapOn:
text: "While using the app"
optional: true
CI/CD 連携
Maestro Cloud を使用した GitHub Actions
Maestro Cloud は、ローカルシミュレーターなしで CI 内に実機を提供します。公式アクションを使用してください。
name: Mobile E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
maestro-e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Build Android APK
- uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Build Android APK
run: |
cd apps/mobile
npx expo prebuild --platform android --no-install
cd android && ./gradlew assembleRelease
- name: Run Maestro Cloud Tests
id: maestro
uses: mobile-dev-inc/action-maestro-cloud@v2
with:
api-key: ${{ secrets.MAESTRO_API_KEY }}
app-file: apps/mobile/android/app/build/outputs/apk/release/app-release.apk
workspace: .maestro
include-tags: ci
# Access results
# ${{ steps.maestro.outputs.MAESTRO_CLOUD_CONSOLE_URL }}
# ${{ steps.maestro.outputs.MAESTRO_CLOUD_UPLOAD_STATUS }}
# ${{ steps.maestro.outputs.MAESTRO_CLOUD_FLOW_RESULTS }}
タグベースのフローフィルタリング
タグを使用して、CI とローカルのどちらでどのテストを実行するかを制御します。
# In your flow file header
appId: com.myapp
tags:
- ci
- smoke
---
- launchApp
# ... test steps
# Run only CI-tagged flows locally
maestro test --include-tags ci .maestro/
# Exclude work-in-progress flows
maestro test --exclude-tags wip .maestro/
Docker を使用したローカル CI(Android のみ)
FROM openjdk:17-slim
RUN curl -Ls "https://get.maestro.mobile.dev" | bash
ENV PATH="/root/.maestro/bin:${PATH}"
COPY .maestro/ /app/.maestro/
WORKDIR /app
CMD ["maestro", "test", ".maestro/"]
Note: iOS のテストは Docker では実行できません(macOS が必要です)。CI での iOS には Maestro Cloud を使用してください。
Maestro Cloud
Maestro Cloud は、ローカルシミュレーターのセットアップなしで実機上でテストを実行します。
セットアップ
- cloud.maestro.dev でアカウントを作成する
- ダッシュボードから API キーを生成する
- CI プロバイダーの
MAESTRO_API_KEYシークレットとして保存する
CLI からの実行
# Upload and run on Maestro Cloud
maestro cloud --api-key $MAESTRO_API_KEY \
--app-file ./app-release.apk \
.maestro/
# With tag filtering
maestro cloud --api-key $MAESTRO_API_KEY \
--app-file ./app-release.apk \
--include-tags smoke \
.maestro/
重要なポイント
- iOS テスト: Maestro Cloud でサポートされています(ローカルの実機では不可)
- Android テスト: ローカルの実機と Maestro Cloud の両方
- 結果: 動画録画、ログ、スクリーンショットを含むダッシュボード
- CI 出力:
MAESTRO_CLOUD_CONSOLE_URL、MAESTRO_CLOUD_FLOW_RESULTS
Maestro MCP サーバー
Maestro MCP サーバー は、Maestro のコマンドセット全体を Model Context Protocol ツールとして公開し、AI エージェントが YAML を書くだけでなく、テストを実行してデバイスと直接対話することを可能にします。
このスキルを補完する仕組み
| このスキル | Maestro MCP | |
|---|---|---|
| 役割 | 正しいパターンを教える | 実行時環境を提供する |
| レイヤー | 作成(適切な YAML を書く) | 実行(実行、タップ、アサート、スクリーンショット) |
| 出力 | より良いテストファイル | ライブデバイスとの対話 |
両方を併用してください。このスキルは AI が正しいテストを書くことを保証し、MCP はそれを即座に実行し、失敗を確認し、反復することを可能にします。
セットアップ
MCP は Maestro CLI に同梱されています — 追加のインストールは不要です。
# Verify it's available
maestro mcp
Claude Code — プロジェクトの .mcp.json またはグローバル設定に追加します。
{
"mcpServers": {
"maestro": {
"command": "maestro",
"args": ["mcp"]
}
}
}
Claude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json(macOS)に追加します。
{
"mcpServers": {
"maestro": {
"command": "maestro",
"args": ["mcp"]
}
}
}
Cursor、Windsurf、VS Code、JetBrains IDE でもサポートされています。IDE 固有のセットアップについては Maestro MCP ドキュメント を参照してください。
主な機能
MCP サーバーは、カテゴリ別に整理された 47 個のツールを公開しています:
| カテゴリ | ツール | 例 |
|---|---|---|
| UI 操作 | タップ、スワイプ、スクロール、長押し | tapOn、scrollUntilVisible |
| テキスト入力 | 入力、削除、貼り付け、コピー | inputText、eraseText |
| アサーション | 可視性、AI による検証 | assertVisible、assertWithAI |
| アプリのライフサイクル | 起動、停止、状態のクリア | launchApp、clearState |
| デバイス制御 | 位置情報、画面の向き、機内モード | setLocation、hideKeyboard |
| フロー制御 | フローの実行、繰り返し、スクリプトの評価 | runFlow、evalScript |
| メディア | スクリーンショット、録画 | takeScreenshot、startRecording |
| AI による機能 | 視覚的なアサーション、欠陥検出 | assertWithAI、assertNoDefectsWithAI |
Write-Run-Fix ループ
このスキルと MCP の両方が有効な場合、AI は次のことができます:
- このスキルのパターンを使用してテスト YAML を 書く
- MCP の
runFlow/launchAppと操作ツールを使ってそれを 実行する - スクリーンショットとアサーションを通じて失敗を 確認する
- YAML を 修正 して再実行する — すべて 1 つの会話の中で
デバッグ
maestro test --debug .maestro/test.yaml # Step through interactively
maestro record .maestro/test.yaml # Record as video
maestro studio # Interactive UI builder
maestro hierarchy # View element tree
スクリーンショットは ~/.maestro/tests/{timestamp}/ に保存されます。
新しいテストのためのチェックリスト
[ ] Unique test email (maestro-{feature}@example.com)
[ ] Selector strategy chosen (testID for i18n apps, text for single-language — see Pattern 1)
[ ] Selectors use state properties where relevant (enabled, checked — see Pattern 10)
[ ] Similar elements distinguished with relative selectors, not index (see Pattern 11)
[ ] Auth pre-flight pattern used (auth-loaded)
[ ] Post-launch swipe added (iOS crash prevention)
[ ] Both auth states handled (adaptive flows)
[ ] Native alerts dismissed after mutations
[ ] Short timeouts for optimistic updates (3-5s)
[ ] Sub-flows created for reusable sequences
[ ] Descriptive screenshots at key points
[ ] Header comment with prerequisites
[ ] Added to README.md test table
[ ] Mock API server started if backend-dependent
[ ] Tags added for CI filtering (ci, smoke, wip)
よくあるエラー
| エラー | 原因 | 修正方法 |
|---|---|---|
| 「Unable to locate Java Runtime」 | Java が PATH にない | export JAVA_HOME=/opt/homebrew/opt/openjdk@17/... |
| タップ後に「Element not found」 | ネイティブのアラートがブロックしている | tapOn: text: "OK" optional: true を追加する |
| OTP の数字が入力されない | 自動フォーカスの干渉 | 個別の otp-input-N testID を使用する |
| テストは成功するが何も起こらない | optional: true の誤用 | optional は本当に任意のアクションにのみ使用する |
| 可視性で「Assertion is false」 | 要素がまだレンダリングされていない | タイムアウトを増やすか、testID が存在するか確認する |
| スクリプトの出力が空 | JS API が間違っている | fetch() ではなく http.get() を使用する |
| clearState 後に認証状態が不安定 | iOS のキーチェーンがクリアされていない | clearState を使用せず、適応型フローを使用する |
| kAXErrorInvalidUIElement クラッシュ | コールドブートの競合(iOS) | 起動後のスワイプに遅延を追加する |
| ローディングスピナー / 空の画面 | API サーバーが実行されていない | テストの前にモック API サーバーを起動する |
| 権限ダイアログがブロックする(Android) | システムダイアログが閉じられていない | tapOn: text: "Allow" optional: true を追加する |