Automated testing for React Native mobile applications using Maestro, Detox, and Appium. Use this skill when testing React Native apps, writing mobile test flows, running UI tests, debugging mobile app failures, or automating mobile testing workflows. Supports iOS and Android platforms.
Test React Native mobile applications using the rn-testing MCP tools with Maestro, Detox, or Appium frameworks.
MCP Tools Available:
maestro_run_flow / maestro_test - YAML-based mobile UI testingdetox_test - Gray-box React Native testingappium_start_session / appium_find_element / appium_tap / appium_input_text - Cross-platform WebDriver automationget_test_results - View test execution historyHelper Scripts Available:
scripts/with_device.py - Manage device/emulator lifecyclescripts/generate_flow.py - Generate Maestro YAML flows from templatesscripts/run_test_suite.py - Run multiple tests with formatted outputscripts/capture_screen.py - Capture screenshots during testingAlways run scripts with --help first to see usage. These scripts handle complex workflows reliably without cluttering context.
User task → Which framework should you use?
│
├─ New to mobile testing / Working with AI agents?
│ └─ Use Maestro (simplest, native MCP support)
│
├─ React Native-only project with source code access?
│ └─ Use Detox (fastest for RN, automatic synchronization)
│
└─ Multi-framework app or black-box testing?
└─ Use Appium (most flexible, cross-platform)
Once framework is chosen → What's your goal?
│
├─ Write new test?
│ ├─ Maestro → Create YAML flow file, then use maestro_run_flow
│ ├─ Detox → Create .test.js file, then use detox_test
│ └─ Appium → Start session, find elements, interact
│
├─ Run existing tests?
│ ├─ Maestro → Use maestro_test with flow paths
│ ├─ Detox → Use detox_test with configuration
│ └─ Appium → Execute session workflow
│
└─ Debug test failure?
└─ Use get_test_results to view recent executions
Maestro is the recommended framework for AI agents due to its simplicity and native MCP support.
Option A: Generate from template (fastest)
python scripts/generate_flow.py login --app-id com.myapp --output flows/login.yaml
Option B: Write manually
Save as flows/login.yaml:
appId: com.myapp
---
- launchApp
- tapOn: "Login"
- inputText: "[email protected]"
- tapOn: "Password Field"
- inputText: "password123"
- tapOn: "Submit"
- assertVisible: "Welcome Screen"
React Native Tip: Use testID props in your components to make elements easily targetable:
<Button testID="login-button" title="Login" />
Then reference in Maestro:
- tapOn:
id: "login-button"
Option A: With device management (recommended)
python scripts/with_device.py --platform android --device "Pixel_7_API_34" -- \
maestro test flows/login.yaml
Option B: Use MCP tool directly (device must be running)
maestro_run_flow({
flow_file: "flows/login.yaml",
response_format: "markdown"
})
get_test_results({
framework: "maestro",
limit: 5
})
For more Maestro patterns, see maestro-patterns.md
Use when: Testing a specific user scenario (login, checkout, onboarding)
# Generate flow from template
python scripts/generate_flow.py login --app-id com.myapp --output flows/login.yaml
# Run with device management
python scripts/with_device.py --platform android --device "Pixel_7_API_34" -- \
maestro test flows/login.yaml
Or use MCP tools:
maestro_run_flow({
flow_file: "flows/checkout.yaml",
app_id: "com.myapp" // optional
})
Use when: Running multiple test scenarios or regression testing
# Run suite with formatted output
python scripts/run_test_suite.py flows/ --stop-on-failure
Or use MCP tools:
maestro_test({
flow_paths: [
"flows/login.yaml",
"flows/navigation.yaml",
"flows/checkout.yaml"
]
})
Use when: Developing tests iteratively
maestro_test({
flow_paths: ["flows/feature.yaml"],
continuous: true // Reruns on file changes
})
Use when: Testing against different API endpoints or configurations
maestro_run_flow({
flow_file: "flows/api-test.yaml",
env: {
"API_URL": "https://staging.api.com",
"API_KEY": "test-key"
}
})
npm install -g detox-cli.detoxrc.js configuration file in projectdetox build --configuration ios.sim.debugdetox_test({
configuration: "ios.sim.debug"
})
detox_test({
configuration: "android.emu.debug",
test_name: "e2e/login.test.js"
})
detox_test({
configuration: "ios.sim.debug",
cleanup: false, // Don't uninstall app
reuse: true // Reuse existing installation
})
Note: Requires Appium server running (appium command)
// 1. Start session
const sessionResult = await appium_start_session({
platform: "android",
app_path: "/path/to/app.apk",
device_name: "Android Emulator"
})
// Extract session_id from sessionResult
const sessionId = "session-abc123"
// 2. Find email input
const emailResult = await appium_find_element({
session_id: sessionId,
strategy: "accessibility_id",
value: "email-input"
})
// Extract element_id
const emailId = "element-xyz789"
// 3. Input email
await appium_input_text({
session_id: sessionId,
element_id: emailId,
text: "[email protected]"
})
// 4. Find and tap submit button
const submitResult = await appium_find_element({
session_id: sessionId,
strategy: "accessibility_id",
value: "submit-button"
})
const submitId = "element-def456"
await appium_tap({
session_id: sessionId,
element_id: submitId
})
Maestro:
- launchApp
- tapOn: "Login"
- inputText: "[email protected]"
- tapOn: "Password"
- inputText: "password123"
- tapOn: "Submit"
- assertVisible: "Home"
React Native TestID Setup:
<TextInput testID="email-input" />
<TextInput testID="password-input" secureTextEntry />
<Button testID="submit-button" onPress={handleLogin} />
Maestro:
- tapOn: "Submit"
- assertVisible: "Error: Email required"
- tapOn:
id: "email-input"
- inputText: "[email protected]"
- tapOn: "Submit"
- assertNotVisible: "Error"
Maestro:
- launchApp
- tapOn: "Profile"
- assertVisible: "Profile Screen"
- tapOn: "Settings"
- assertVisible: "Settings Screen"
- back
- assertVisible: "Profile Screen"
Maestro:
- launchApp
- assertVisible: "Product List"
- tapOn: "iPhone 15"
- assertVisible: "Product Details"
- tapOn: "Add to Cart"
- assertVisible: "Added to Cart"
get_test_results({
framework: "all",
limit: 10,
response_format: "markdown"
})
Common failure patterns:
Element not found:
Timing issues:
Wrong configuration:
// Good - Stable testID
<Button testID="submit-button" title="Submit" />
// Avoid - Text may change
<Button title="Submit Form Now" />
# Good - Single responsibility
# File: flows/login.yaml
- launchApp
- tapOn: "Login"
- # Login steps only
# Avoid - Multiple concerns in one file
- launchApp
- tapOn: "Login"
- # Login + Navigation + Checkout...
flows/
├── auth/
│ ├── login.yaml
│ ├── signup.yaml
│ └── logout.yaml
├── checkout/
│ ├── add-to-cart.yaml
│ └── complete-order.yaml
└── navigation/
└── main-menu.yaml
# Good
- assertVisible: "Welcome, John!"
# Avoid
- assertVisible: "Text 1"
Priority order:
Need help choosing? See frameworks-comparison.md for detailed comparison and recommendations.
Quick Summary:
Manages emulator/simulator startup, waits for ready, runs command, then cleanup.
# Run tests with device management
python scripts/with_device.py --platform android --device "Pixel_7_API_34" -- \
maestro test flows/
# iOS
python scripts/with_device.py --platform ios --device "iPhone 15 Pro" -- \
maestro test flows/
# Use existing device (don't start/stop)
python scripts/with_device.py --platform android --use-existing -- \
maestro test flows/
Generate Maestro YAML flows from templates.
# List available templates
python scripts/generate_flow.py --list
# Generate login flow
python scripts/generate_flow.py login --app-id com.myapp --output flows/login.yaml
# Generate form flow
python scripts/generate_flow.py form --fields email,password,name --output flows/signup.yaml
# Generate navigation flow
python scripts/generate_flow.py navigation --screens Home,Profile,Settings --output flows/nav.yaml
Run multiple tests with formatted output and summary.
# Run all flows in directory
python scripts/run_test_suite.py flows/
# Run specific flows
python scripts/run_test_suite.py flows/login.yaml flows/checkout.yaml
# Stop on first failure
python scripts/run_test_suite.py flows/ --stop-on-failure
# Run with specific device
python scripts/run_test_suite.py flows/ --device "iPhone 15 Pro"
Capture screenshots from devices during testing.
# Capture from Android
python scripts/capture_screen.py --platform android --output screenshots/screen1.png
# Capture from iOS
python scripts/capture_screen.py --platform ios --device "iPhone 15 Pro" --output screenshots/screen1.png
# Auto-generate filename with timestamp
python scripts/capture_screen.py --platform android --auto-name --output-dir screenshots/
The assets/ directory contains example flow files you can use as templates:
login-flow.yaml - Login form examplenavigation-flow.yaml - Screen navigation exampleCopy and modify these for your app.
Before testing, ensure:
For Maestro:
curl -Ls https://get.maestro.mobile.dev | bash)For Detox:
npm install -g detox-cli).detoxrc.js configuration existsdetox build)For Appium:
npm install -g appium)appium driver install uiautomator2 / xcuitest)appium)"Command not found" errors:
which maestro / which detox / which appium"Device not found" errors:
device_id parameter to specify exact deviceadb devices (Android) or xcrun simctl list (iOS)Test timeouts:
Element not found:
maestro studio to inspect current screen