By Jochen D.
Mobile test automation has a harder version of every problem web testing has. The device matters, not just the browser. The app has to be built and installed before anything runs. Gestures are physical. And the feedback loop is slow enough that a badly chosen framework costs you for years.
This page is about the two decisions that actually determine how the next few years go: which framework to write in, and what you run it on.
Last reviewed 31 August 2026.
The framework decision
There are three realistic answers, and they differ less on features than on who is going to maintain the suite and how many platforms it has to cover.
- Appium if you need one suite across iOS and Android, or your test engineers are not the app developers.
- Espresso and XCUITest if the app team owns the tests and speed matters more than sharing code across platforms.
- Maestro if you want flows written quickly and readably, and you can live with a smaller ecosystem.
The wrong reason to choose is a feature comparison table. The right reasons are who writes the tests, whether the same tests must run on both platforms, and how much tolerance you have for maintenance.
Appium: one API, every platform
Appium drives native, hybrid and mobile web apps through the WebDriver protocol, which means the same client libraries and much of the same mental model as Selenium. One test suite, written in whatever language your team already uses, covering both platforms.
Two things to know before starting today. Since Appium 2, drivers are installed separately rather than bundled, so a working setup means appium driver install uiautomator2 for Android and appium driver install xcuitest for iOS. And Appium 3, currently 3.7.0, removed the old touch endpoints entirely: gestures now go through the W3C Actions API or driver-specific commands such as mobile: swipeGesture. A lot of the Appium material online predates that change and will not run.
The trade: the widest platform and language coverage of the three, and the slowest. Appium drives the app from outside the process, through a server, which costs you both wall-clock time and a category of flakiness the native runners do not have.
We cover setup in getting started with Appium, gestures in using touch actions with Appium, and the version differences in the Appium migration guide.
Espresso and XCUITest: the native runners
Espresso on Android and XCUITest on iOS are the platform vendors' own frameworks. They run in or alongside the app process, which makes them substantially faster and less flaky than anything driving from outside.
Espresso in particular has a real structural advantage: it synchronises with the UI thread, so it knows when the app is idle rather than guessing. That single property removes most of the arbitrary waiting that makes other mobile suites unreliable.
The trade: two codebases, two languages, two sets of skills. You are writing the Android suite in Kotlin or Java and the iOS suite in Swift, and neither shares anything with the other. For an app team that already works in both, that is barely a cost. For a separate QA team, it is a large one.
Maestro: the declarative option
Maestro takes a different approach: flows are YAML rather than code, and the runner has built-in tolerance for the timing problems that dominate mobile testing, so you write far fewer explicit waits.
The practical effect is that a flow is short enough to read in one screen and quick enough to write that people actually write them. That matters more than it sounds, because the most common failure of a mobile test strategy is not a bad framework, it is a suite nobody adds to.
The trade: a smaller ecosystem, and YAML's usual ceiling. When a flow needs real branching logic, you feel the absence of a programming language. It is also the youngest of the three, so there is less written about it when you get stuck.
We cover it in running Maestro tests in the cloud.
Choosing between them
A summary, with the caveat that the right answer depends on your team more than on the tools:
- Cross-platform coverage: Appium clearly. Maestro handles both platforms; the native runners do not.
- Speed and stability: Espresso and XCUITest, by a wide margin. In-process beats out-of-process.
- Time to a first working test: Maestro, usually by a lot.
- Existing skills: Appium if your team knows Selenium. The native runners if your team writes the app.
- Ecosystem and hiring: Appium, which has by far the most material, plugins and people who have used it.
Mixing is normal and often correct. Native runners for the fast unit-adjacent UI checks that run on every commit, and Appium or Maestro for the cross-platform end-to-end journeys that run less often.
Emulators, simulators and real devices
The second decision matters as much as the framework, and it is not either/or.
Emulators and simulators are fast, free and parallelise trivially. They are the right target for the bulk of a suite, and for anything running on every commit.
Real devices catch what emulation cannot, and the list is not exotic: actual GPU and rendering behaviour, real touch and gesture handling, camera, biometrics, push notifications, genuine network conditions, thermal throttling and battery behaviour, and manufacturer skins on Android that change platform behaviour in ways the stock emulator never shows. If your bug reports come disproportionately from one vendor's phones, that is what this is for.
The usual split is a large fast suite on emulators, plus a smaller suite of critical journeys on real devices covering the handful of models your analytics say actually matter. You can see the current device and OS coverage on the mobile testing pages rather than taking a number from an article, since the fleet changes.
What changes at scale
A mobile suite that works on one laptop develops new problems when it becomes a hundred tests in CI.
- Wall-clock time stops being linear. Mobile tests are slow individually, so the only real lever is running them at once. A suite that takes ninety minutes serially can take a few minutes across enough devices. The parallel calculator shows the shape of that trade for a given concurrency.
- Device supply becomes the constraint. Maintaining a physical device lab means buying, charging, updating, re-flashing and replacing hardware, which is a real ongoing job rather than a one-off purchase. That is the main argument for a hosted fleet.
- Build and upload get onto the critical path. At scale the app binary has to be built, uploaded and installed before anything runs, and that step is easy to leave unoptimised.
- Failure triage dominates. With enough tests on enough devices, the bottleneck moves from running tests to working out what a wall of red means. Video, logs and per-device artefacts stop being nice to have.
Why mobile suites go flaky
Mobile flakiness has causes web testing mostly does not, and knowing them shortens the debugging considerably:
- Animations and transitions. The element is present but still moving. This is the single most common cause, and it is why Espresso's idle-thread synchronisation is such an advantage.
- Permission and system dialogs. A first-run location or notification prompt appears on a fresh install and not on a warm one, so the test passes locally and fails in CI.
- Device state carried between tests. Logged-in sessions, cached data and stored files survive unless you reset them.
- Real network conditions. A device on real networking has latency and packet loss that localhost does not.
- Keyboards. The software keyboard covering the element you are about to tap is a genuinely frequent failure, and an easy one to miss because it is invisible in a log.
The general treatment is the same as anywhere: assert on state rather than sleeping, reset device state between tests, and treat a retry that passes as a bug to investigate rather than a pass. See how to fix flaky tests.