DEV Community

Cover image for Maestro: Mobile UI Testing Without the Pain - Introduction
ArshTechPro
ArshTechPro

Posted on

Maestro: Mobile UI Testing Without the Pain - Introduction

If you have ever written mobile end-to-end tests, you probably know the routine. You set up Appium, fight with drivers and capabilities, write a test that passes locally, and then watch it fail in CI because an animation took 200ms longer than expected. You add a sleep(2). Then another. Six months later nobody trusts the suite.

Maestro is an open-source framework built to make that experience less painful. This article covers what it is, how it works, what you should know before adopting it, and how it stacks up against the other free and paid options.

This is Part 1 of a two-part series. Part 2 is a hands-on deep dive with a small SwiftUI app and a complete test suite.

What is Maestro?

Maestro is a UI and end-to-end testing framework for Android, iOS and web apps. Instead of writing tests in Java, Kotlin, Swift or JavaScript, you describe user journeys (called Flows) in YAML:

appId: com.example.myapp
---
- launchApp
- tapOn: "Sign in"
- tapOn: "Email"
- inputText: "jane@example.com"
- tapOn: "Password"
- inputText: "hunter2"
- tapOn: "Continue"
- assertVisible: "Welcome, Jane"
Enter fullscreen mode Exit fullscreen mode

That is a complete, runnable test. No page objects, no driver setup code, no explicit waits.

How Maestro works (and why it matters)

Maestro is a black-box tool. It does not link into your app, does not need an SDK, and does not need your source code. It drives the device the way a user would, and it reads the screen through the platform's accessibility layer and view hierarchy.

On iOS, Maestro installs a small XCTest-based driver on the simulator. On Android, it installs a driver app that talks to UIAutomator. The CLI sends commands to the driver, reads back the UI hierarchy, and decides what to do next.

Three design choices make it feel different from older tools:

1. Automatic waiting. After each command, Maestro waits for the screen to settle before moving on. If you tapOn: "Continue" and the button is not visible yet, it keeps looking for a while before failing. In practice this removes most of the sleep() calls you would write elsewhere.

2. Flakiness tolerance. Mobile UIs are noisy: taps sometimes do not register, elements shift during animations, keyboards appear and disappear. Maestro's commands are designed to retry and re-check rather than fail on the first miss.

3. Interpreted, not compiled. Flows are read at runtime. Change a YAML file, rerun, and see the result in seconds. There is no test target to rebuild.

Because it operates "at arm's length", Maestro can also do things in-process tools cannot: tap system permission dialogs, open Safari or Settings, and come back to your app in the same flow.


The ecosystem: CLI, Studio, MCP and Cloud

Maestro is more than one binary. Here is what each piece does and what it costs.

Component What it does Cost Open source?
Maestro CLI Runs flows locally and in CI. The core of everything. Free Yes (Apache 2.0)
Maestro Studio (desktop) Visual IDE: inspect elements, record interactions, build flows without the terminal. Available for macOS, Windows and Linux. Free No
Maestro MCP Built into the CLI (maestro mcp). Lets AI coding agents such as Claude Code, Cursor, Copilot, Codex and Gemini run flows, inspect the screen and write tests. Free Yes
Maestro Cloud Hosted parallel execution on managed simulators, emulators and browsers. Paid No

A note on Studio: older versions of the CLI shipped a browser-based Studio (maestro studio). That was removed in CLI 2.6.0 in favor of the desktop app. If you find older tutorials using maestro studio, that is why they no longer match.

A note on AI: the old maestro chat command was discontinued in 2.7.0. The supported path for AI-assisted test authoring is now Maestro MCP.


Getting started in five minutes

1. Install

You need Java 17 or newer.

java -version
Enter fullscreen mode Exit fullscreen mode

Then install the CLI on macOS, Linux or Windows (WSL):

curl -fsSL "https://get.maestro.mobile.dev" | bash
Enter fullscreen mode Exit fullscreen mode

Or, on macOS with Homebrew:

brew tap mobile-dev-inc/tap
brew install maestro
Enter fullscreen mode Exit fullscreen mode

Verify:

maestro --version
Enter fullscreen mode Exit fullscreen mode

2. Start a device

For iOS you need Xcode with at least one Simulator runtime installed. For Android, an emulator from Android Studio. Maestro can also start one for you:

maestro list-devices
maestro start-device --platform ios
Enter fullscreen mode Exit fullscreen mode

3. Write a flow

Save this as contacts.yaml. It uses the built-in iOS Contacts app, so you do not need your own app yet:

appId: com.apple.MobileAddressBook
---
- launchApp
- tapOn: "Add"
- tapOn: "First name"
- inputText: "Ada"
- tapOn: "Last name"
- inputText: "Lovelace"
- tapOn: "Done"
- assertVisible: "Ada Lovelace"
Enter fullscreen mode Exit fullscreen mode

Button and field labels in Apple's built-in apps can change between iOS versions. If a step fails, open Maestro Studio or run maestro hierarchy to see the exact labels on your simulator.

4. Run it

maestro test contacts.yaml
Enter fullscreen mode Exit fullscreen mode

You will see each step printed as it runs, with a pass or fail marker. On failure, Maestro saves a screenshot, the view hierarchy and logs so you can see what was on screen at the moment it broke.


What is important for developers to know

These are the things that are easy to miss in the marketing material but matter once you adopt Maestro on a real project.

1. iOS testing targets Simulators

Maestro's officially supported local iOS target is the Simulator. You need a Simulator build (.app), not an App Store or Ad Hoc .ipa. Support for physical iPhones has been one of the most requested features in the repository for a long time. Today, if you need real iOS hardware, your options are third-party projects or device clouds that have added Maestro support on physical devices (TestingBot is one example). Check the current docs before you plan around real devices.

Android has no such limitation: emulators and USB-connected physical devices both work.

2. Accessibility identifiers make tests stable

Maestro can find elements by visible text, which is great for getting started. For a test suite you plan to keep, add stable identifiers:

  • SwiftUI / UIKit: accessibilityIdentifier
  • Android: resource-id / testTag
  • React Native: testID
  • Flutter: Semantics(identifier: ...) or keys exposed through semantics

Then target them with id:

- tapOn:
    id: "loginButton"
Enter fullscreen mode Exit fullscreen mode

Text selectors break when copy changes or when you test in another language. IDs do not.

3. YAML is simple until it isn't

For linear user journeys, YAML is a joy. For complex logic (loops with conditions, computed data, calling a backend to seed state), you will reach for Maestro's JavaScript support (runScript, evalScript, ${...} expressions and an http client). It works, but it is not a full programming language with an IDE, types and a debugger. If your test strategy depends on heavy logic, factor that into your choice.

4. Black-box means no app internals

You cannot call into your app's code, stub a network layer from inside the process, or read internal state. If you need that, it has to happen outside Maestro: a test backend, launch arguments, or a debug-only screen. Maestro's own built-in network mocking was deprecated years ago.

5. It is not a replacement for unit or in-process UI tests

Maestro is excellent for high-level user journeys: sign-up, checkout, onboarding, critical paths. It is not the right tool for testing every state of a single component. Keep XCTest/XCUITest, Espresso or your framework's widget tests for that.

6. CI is straightforward, scaling is the hard part

Running Maestro in CI is just maestro test plus JUnit or HTML reports. Scaling is where costs appear. iOS Simulators need macOS machines. Locally you can shard across several simulators (parallel iOS simulator execution became reliable in CLI 2.6.0), but at some point teams either buy more Mac runners or move to a cloud.

7. The project moves fast

The CLI ships frequently, and some releases deprecate flags. For example, --ios-version and --android-api-level are being replaced by --device-os and --device-model. Pin your Maestro version in CI and read the changelog when upgrading.


How Maestro compares to other tools

Free and open-source frameworks

Tool Platforms Test language Approach Setup effort Best for
Maestro iOS, Android, Web YAML (+ JS) Black-box Minutes Cross-platform user journeys, mixed teams, fast feedback
Appium iOS, Android, Web, Windows, more Java, JS/TS, Python, C#, Ruby Black-box (WebDriver) Hours to days Large cross-platform suites, teams with existing Selenium skills
XCUITest iOS only Swift / Obj-C In Xcode, separate test process Low (built into Xcode) iOS-only teams who want native tooling and real-device support
Espresso Android only Kotlin / Java Gray-box, in-process Low Android-only teams, fast and stable component-level UI tests
Detox iOS, Android (React Native) JS / TS Gray-box, syncs with RN internals Moderate to high React Native apps that need tight synchronization
Flutter integration_test / Patrol iOS, Android (Flutter) Dart In-process, Patrol adds native interaction Moderate Flutter teams who want tests in Dart

Maestro vs Appium. Appium is the most flexible and the most mature: any language, any platform, huge ecosystem, real iOS devices. It is also the most work to set up and maintain, and tests tend to need explicit waits. Maestro trades flexibility for speed of authoring and lower flakiness. Many teams that move from Appium to Maestro do so because test maintenance, not test execution, was their bottleneck.

Maestro vs XCUITest. XCUITest is Apple's own framework. It is fast, deeply integrated with Xcode and supports real devices. But tests are Swift code that needs a test target, compilation and more boilerplate, and it only covers iOS. If you ship on both platforms, you would also need Espresso and two separate suites. Maestro gives you one suite, often with shared flows across platforms.

Maestro vs Detox. Detox is gray-box: it knows when React Native is idle, which makes it very stable for RN apps. That same coupling makes setup harder and limits it to React Native. Maestro works with any framework but synchronizes based on what is on screen rather than JS thread state.

Paid platforms and device clouds

Most paid offerings are infrastructure: they run tests you have already written on hosted devices. Prices below are list prices published at the time of writing (October 2026). Always confirm on the vendor's pricing page.

Service What you get Pricing model Maestro support
Maestro Cloud Official hosted runner for Maestro flows, parallel execution, artifacts, notifications $250 per device per month for iOS or Android, $125 per browser per month for web; enterprise plans available; 7-day free trial Native
BrowserStack App Automate Large real-device cloud for Appium, Espresso, XCUITest and more Per parallel session; App Automate starts around $199 per parallel per month billed annually Yes, via integration
Sauce Labs Real devices and emulators, enterprise features Per parallel test with unlimited users and minutes Check current docs
TestingBot Real devices and simulators, Maestro CLI integration Subscription Yes, including physical iOS devices
DeviceCloud (devicecloud.dev) Maestro-focused cloud runner Pro plan from $25 per month including test credits, usage on top Yes
AWS Device Farm / Firebase Test Lab Pay-as-you-go device access from the major clouds Metered usage with free tiers or trials Possible with custom setup, not first-class
AI-native testing tools (for example Drizz, Autosana) Natural-language or vision-based test authoring and self-healing Subscription, usually quote-based Not applicable; they are alternatives

A simple way to think about it:

  • Authoring cost (how long tests take to write and maintain): Maestro is among the lowest.
  • Execution cost (where tests run): free on your own machines, then you pay either for Mac CI runners or for a device cloud. The cloud choice is mostly about price per parallel, device coverage (real vs virtual) and where your data is allowed to go.

When to choose Maestro

Maestro is a strong fit if:

  • You ship on both iOS and Android, or plan to.
  • You want end-to-end coverage of critical user journeys without building a test framework.
  • QA engineers, product people or designers should be able to read and contribute to tests.
  • Flaky tests are a bigger problem for you than raw test speed.

Look elsewhere, or combine tools, if:

  • You must run iOS tests on physical devices in your own lab with official support.
  • Your tests need deep access to app internals or in-process mocking.
  • You need thousands of fine-grained UI assertions per component (use XCUITest, Espresso or framework tests for those).
  • Your organization already has a large, healthy Appium suite.

Wrapping up

Maestro's pitch is simple: describe what a user does, in plain YAML, and let the framework handle the waiting and retrying. For most teams the biggest win is not execution speed but the fact that tests are quick to write, easy to read and less likely to become the flaky suite nobody trusts.

In Part 2, we will build a small SwiftUI app with a login screen and a to-do list, then write a full Maestro suite for it: stable selectors, sub-flows, environment variables, conditional logic for permission dialogs, tags, reports and a GitHub Actions workflow.

Resources

Top comments (0)