Maestro × Antigravity — The New Standard for Mobile UI Test Automation
Mobile UI testing has long been a necessary but painful part of app development. Frameworks like XCUITest and Espresso are powerful, but the amount of boilerplate code they require means keeping tests in sync with UI changes can eat up serious engineering time.
Enter Maestro — a YAML-based mobile E2E testing framework that works for both iOS and Android. Its simplicity is precisely what makes it a perfect partner for Antigravity's AI agents. Just tell Antigravity which user flows you want covered, and it will generate working Maestro scenarios in minutes.
This guide walks through everything you need to set up and scale a Maestro + Antigravity mobile testing workflow, from initial installation to full CI/CD integration.
What Is Maestro — and Why Does It Work So Well with AI?
Maestro is an open-source mobile E2E testing framework built by mobile.dev. Its defining feature is YAML-based test definitions — no Swift, no Kotlin, just plain readable configuration.
# Simple login flow test
appId: com.example.myapp
---
- launchApp
- tapOn: "Email"
- inputText: "test@example.com"
- tapOn: "Password"
- inputText: "password123"
- tapOn: "Log In"
- assertVisible: "Home Screen"This simplicity is a superpower when combined with Antigravity. Rather than generating complex test code in a native language, Antigravity can produce valid, working YAML test scenarios just from a description of your app's screens.
Here's a quick comparison of when to choose each framework:
- Maestro: YAML syntax, cross-platform iOS/Android, easy setup, excellent AI generation compatibility
- XCUITest: Swift/Objective-C, iOS only, deep Xcode integration
- Espresso: Kotlin/Java, Android only, officially supported by Google
Maestro is the right choice when you want broad E2E coverage across platforms quickly, and especially when you want AI to generate and maintain those tests over time.
Setup — Installing Maestro and Configuring Your Project
Start by installing Maestro:
# macOS / Linux
curl -Ls "https://get.maestro.mobile.dev" | bash
# Verify installation
maestro --version
# Expected output: Maestro v1.x.xNext, set up a clear directory structure in your Antigravity project:
myapp/
├── maestro/
│ ├── flows/
│ │ ├── login.yaml
│ │ ├── onboarding.yaml
│ │ └── checkout.yaml
│ └── config.yaml
└── src/
└── ...
To help Antigravity understand your testing conventions, add a context file at .antigravity/rules.md (or AGENTS.md):
# Testing Conventions
- E2E tests use Maestro (YAML format)
- Test files go in `maestro/flows/`
- appId: com.example.myapp
- Use accessibility IDs for element targeting when possibleWith this context in place, Antigravity will generate Maestro-compatible test scenarios without any extra prompting.
Generating Test Scenarios with Antigravity's AI Agent
Once your environment is ready, use Antigravity's agent mode to generate test scenarios automatically.
Example Prompt
Antigravity instruction (run in Agent Mode):
Please create Maestro YAML test scenarios in maestro/flows/ for the following user flows:
1. New user signup (email + password input → confirmation → home screen)
2. Product search (keyword input → results → product detail → add to cart)
3. Checkout (cart review → address input → payment selection → order confirmation)
Include edge case scenarios for each flow (empty inputs, network error states, etc.).
Sample Generated Scenario
Here's what Antigravity might produce for the signup flow:
# maestro/flows/signup.yaml
appId: com.example.myapp
---
- launchApp:
clearState: true
- tapOn: "Create Account"
- tapOn:
id: "email_input"
- inputText: "newuser@example.com"
- tapOn:
id: "password_input"
- inputText: "SecureP@ss123"
- tapOn:
id: "confirm_password_input"
- inputText: "SecureP@ss123"
- tapOn: "Continue"
- assertVisible:
text: "Verification email sent"
timeout: 5000
- tapOn: "Skip for now"
- assertVisible:
text: "Home"
timeout: 10000# maestro/flows/signup_error.yaml (error case)
appId: com.example.myapp
---
- launchApp:
clearState: true
- tapOn: "Create Account"
- tapOn: "Continue"
- assertVisible: "Please enter your email address"
- tapOn:
id: "email_input"
- inputText: "not-an-email"
- tapOn: "Continue"
- assertVisible: "Please enter a valid email address"Both happy path and error cases generated — in minutes, not hours.
Running Tests on iOS Simulator / Physical Device
Here's how to run your Maestro tests against an iOS Simulator:
# Launch the simulator (requires Xcode Command Line Tools)
open -a Simulator
# Build and install the app
xcodebuild -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15' build
# Run a single test
maestro test maestro/flows/login.yaml
# Run all flows
maestro test maestro/flows/
# Expected output:
# ✅ Launch App
# ✅ Tap on "Email"
# ✅ Input "test@example.com"
# ✅ Tap on "Log In"
# ✅ Assert visible "Home Screen"
# Test PASSED in 8.3sWhen a test fails, Maestro automatically captures a screenshot and screen recording at the point of failure. You can feed these directly to Antigravity for diagnosis:
Antigravity instruction:
The following Maestro test failed:
- Flow: maestro/flows/login.yaml
- Error: assertVisible timeout — "Home Screen" not found (10000ms)
- Screenshot: [attached]
Please analyze the failure and suggest a fix — either in the test YAML or the app's login implementation.
Running Tests on Android Emulator / Physical Device
The same YAML files work on Android with minimal changes:
# Start Android emulator
emulator -avd Pixel_7_API_34
# Build and install APK
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk
# Run with Maestro (same command as iOS)
maestro test maestro/flows/login.yaml
# Switch appId via environment variable for cross-platform runs
MAESTRO_APP_ID=com.example.myapp.android maestro test maestro/flows/login.yamlHandling Platform-Specific UI Differences
When iOS and Android have different UI layouts, use conditional flows:
# maestro/flows/platform_specific.yaml
appId: com.example.myapp
---
- launchApp
- runFlow:
when:
platform: iOS
file: ios_specific_steps.yaml
- runFlow:
when:
platform: Android
file: android_specific_steps.yaml
- assertVisible: "Shared UI Element"You can ask Antigravity: "Generate a Maestro flow that handles platform differences between iOS and Android for the checkout screen," and it will produce the right conditional structure automatically.
CI/CD Integration with GitHub Actions
To run your mobile tests on every PR, add a GitHub Actions workflow. Let Antigravity generate the YAML:
# .github/workflows/maestro-tests.yml
name: Mobile UI Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
ios-ui-tests:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install Maestro
run: curl -Ls "https://get.maestro.mobile.dev" | bash
- name: Add Maestro to PATH
run: echo "$HOME/.maestro/bin" >> $GITHUB_PATH
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.0'
- name: Build and Install App
run: |
xcodebuild -scheme MyApp \
-destination 'platform=iOS Simulator,name=iPhone 15' \
build
- name: Run Maestro Tests
run: maestro test maestro/flows/ --format junit --output test-results.xml
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: maestro-test-results
path: |
test-results.xml
~/.maestro/tests/For a deeper dive into CI/CD pipeline automation for mobile apps, check out Antigravity × Firebase App Distribution — Automating iOS/Android Beta Distribution with CI/CD.
Common Errors and How to Fix Them with Antigravity
Error 1: Element Not Found
Error: java.lang.IllegalArgumentException: Element not found: "Log In"
Cause: Text matching is brittle — spacing differences, capitalization, or localization can cause mismatches.
Ask Antigravity: "The element lookup by text is failing. Please refactor this test to use accessibility IDs or labels instead."
# Before: fragile text matching
- tapOn: "Log In"
# After: reliable ID-based targeting
- tapOn:
id: "login_button"
# Or using accessibility label
- tapOn:
label: "Log In Button"Error 2: Timeout
Error: Timeout waiting for element to be visible: "Home Screen" (10000ms)
Fix: Increase the timeout or add an explicit wait:
- assertVisible:
text: "Home Screen"
timeout: 20000 # Extended from 10000msError 3: Keyboard Obscures Elements
Fix: Dismiss the keyboard after text input:
- inputText: "test@example.com"
- hideKeyboard
- tapOn: "Continue"These patterns can be documented in your AGENTS.md so Antigravity applies them proactively in future test generation.
Scaling with Maestro Cloud
For testing on real physical devices at scale, Maestro Cloud lets you run tests in parallel across multiple device configurations:
# Upload and run on Maestro Cloud
maestro cloud --apiKey YOUR_MAESTRO_CLOUD_KEY maestro/flows/
# Expected output:
# Uploading flows...
# Running on: iPhone 15 Pro (iOS 26.0)
# Running on: Google Pixel 8 (Android 15)
# Running on: Samsung Galaxy S24 (Android 14)
# All tests PASSEDIf your goal is to build a full automated release pipeline that goes from test → build → store submission, Antigravity × Fastlane: Complete Guide to App Store & Google Play Automated Deployment is worth reading alongside this guide.
Looking back
Combining Maestro's YAML-based simplicity with Antigravity's AI generation capabilities creates a genuinely practical mobile testing workflow — one where you can go from "I need tests for these five flows" to a working CI/CD pipeline in a single afternoon.
The key insight is that YAML's readability makes it the ideal format for AI to generate, review, and modify tests. Instead of fighting complex testing frameworks, you can focus on describing what your app should do and let Antigravity handle the implementation.
Start with one critical flow, build confidence, and expand coverage iteratively. Your future self — staring at a passing test suite on release day — will thank you.