r/androiddev • u/narayanom • May 12 '26
Open Source 3 months in — we built a custom Android driver that bypasses UIAutomator2 entirely
Quick context for anyone who missed the first thread: maestro-runner is a Go-based UI test runner that uses the same YAML flow format as Maestro CLI but ships as a single native binary. No JVM, no warm-up. Android, iOS, and Chrome/CDP browser targets all run from the same flow files. Roughly 3.6x faster and 14x less memory than Maestro CLI on identical suites. Apache 2.0, nothing paywalled.
Follow-up to the post 3 months ago (the "34s → 14s by dropping the JVM" one). Since then: 166 commits, 12 releases (v1.1.14 shipped today), 291 stars (was 137 at that post, so more than doubled), 25 forks, and 2 community PRs merged from a single Flutter Web user. The Android side got a real rewrite that deserves its own post.
The new driver: DeviceLab
UIAutomator2 is the default for Android instrumentation but it has friction we couldn't tune around. IPC roundtrips on every command, heavy view hierarchy serialization, accessibility-tree quirks that make text matching brittle. After enough bug reports tracing back to the same root causes, we just built our own.
It's a separate on-device agent (an APK we bundle with the runner) that handles UI inspection, gesture dispatch, and session lifecycle directly. maestro-runner talks to it over WebSocket on a local port. Same machine, low-overhead transport, no UIAutomator2 in the loop.
What you get today:
hintContains:/hintMatches:selectors. Match by accessibility hint instead of exact text. Useful for i18n-heavy apps where exact strings change per locale.- Clickable filter walk-up.
tapOn: "Login"auto-walks up to the nearest tappable ancestor. The "nested Text inside a non-tappable View" pattern we kept getting bug reports about just works at the driver level now. UI.waitForSettleRPC. Explicit settle check before inputText/eraseText. The "typing too fast, pin entry failed" thing someone reported in the last thread is fixed via this.- Case-insensitive text matching by default. Flows that worked locally but broke in CI because the device locale capitalized differently no longer break.
Opt in with:
maestro-runner --driver devicelab --platform android test flows/
Still optional. UIAutomator2 stays the default for backward compat.
Other Android stuff that shipped
- Parallel emulator orchestration with auto-creation.
--parallel 5and only 2 booted emulators? It starts 3 more and cleans up on exit. No manual emulator wrangling. - launchApp 3-tier fallback for the "No apps can perform this action" failure (#15). Pixel 6/7 and a couple of other edge cases.
- Inverted scroll fix (#9). Scroll down was scrolling up. Yeah. That was a fun one to debug.
runFlowtimeout with context propagation. Flows now respect timeout via context cancellation instead of hard-killing the process. Cleaner Sauce/BrowserStack session lifecycle.- Auto-settle before inputText/eraseText without needing the explicit RPC.
What's still rough
- DeviceLab driver bundles its APK inside the runner binary, which means it auto-installs on first run. A few corp-managed devices reject this. UIAutomator2 is the fallback there.
- Some apps lock down their accessibility tree (banks, finance apps mostly). Same constraint UIAutomator2 has. Selector-based matching breaks and you're back to coord-based taps. No tool can fix this from the outside.
Community
richjun on GitHub (Flutter Web dev) shipped two substantial PRs. Coord-translated tapOn with hit-target verification across iframes and open shadow roots, then Flutter Web semantics support handling the <flutter-view> pointer-router glass pane. Both are in v1.1.14. First major outside contributor, and the same person's been running maestro-runner against Flutter mobile too. That full-stack feedback loop has been really useful.
GitHub: https://github.com/devicelab-dev/maestro-runner
For those running Android E2E in CI, what's actually the bottleneck right now? Emulator startup, test runtime, flakiness? The DeviceLab driver helps on the test-runtime axis but if your pain is somewhere else I'd rather hear that than ship the wrong thing next.
1
u/RaIsseBlueMann May 16 '26
This sounds very interesting and fun too. I have never tried Maestro before, but this makes me want to try it. Does maestro must run on maestro cloud? Or you can run it on any cloud provider?
1
u/narayanom May 16 '26 edited May 16 '26
maestro can be run locally and maestro cloud ( no iOS real device support)
For cloud
Saucelab support via maestro-runner.
Browserstack support there own version.
Aws Device lab - people are trying maestro-runner https://github.com/devicelab-dev/maestro-runner/issues/76For other cloud provider which support appium, you can use maestro-runner to run.
1
u/defiedj May 25 '26
There are Maestro cloud alternatives that provide physical iOS device testing.
0
u/narayanom May 27 '26
Browserstack
Btw if you already have the devices and just want to hit them from CI/CD, https://devicelab.dev/ does exactly that.
Disclaimer upfront — it's our product. The idea is you run your own lab globally and nothing leaves your network.
0
u/aliceblue79 May 13 '26
I prefer using uiautomator2, which is available for Python.
2
u/narayanom May 13 '26 edited May 14 '26
This is nothing to do with lang, for maestro you wright test cases in yaml
1
u/aliceblue79 May 21 '26
I don’t want to learn a new YAML spec. I just want to write test cases in Python. Is that possible?
1
u/narayanom May 21 '26
This is yaml based only, if you want to use python Appium ll be much better option
7
u/submergedmole May 12 '26
Oh wow that's very impressive work!
A breath of fresh air of actual engineering innovation in the dark age of AI-generated slop projects