Demystifying Maestro’s UI Testing Implementation

I first heard about Maestro when it was released in 2022, but I haven’t played with it until now. I’m doing some work with Espresso and UiAutomator for Android testing and was curious how Maestro worked under the hood, so I cloned their git repo and dove into the code.

This quick post shares what I learned about the inner workings of the library with TONS of links to their open source implementation. Feel free to jump to the “How it Works” section below if you already know what Maestro is.

Background – What is Maestro?

It’s a testing framework where you use an easy to read YAML file to create commands that will execute on an Android, iOS or Web app. Here is a quick visual (taken from their website at maestro.mobile.dev) that gives you an idea of what it does.

Maestro Studio

This is an “app” you can run on your on machine to author the YAML for Maestro. It runs a local server and the app is served in a web browser. It continually updates with screenshots from the device and allows you to create commands and run them. My favorite part of it is how they have a nice way to represent the rendered UI tree. Here is a good demo of what that looks like here from Daniel Knott’s YouTube Channel.

How it Works

Maestro installs their own APKs

Their APKs are installed by their CLI tool. (source code) Here is the ADB command showing that the apps are installed after running. 👇

adb shell pm list packages | grep dev.mobile.maestro
package:dev.mobile.maestro.test
package:dev.mobile.maestro

Maestro Starts an On-Device GRPC Server within a Test

They start a @Test in their Instrumentation APK which then starts an on-device Netty GRPC server that runs in process and just runs the test as long as the server is running. (source code)

This test is started via an adb instrument command. (source code) By running the server inside the app, it gives them access to use UiAutomator just like any Android @Test and additionally allows the in-process code to respond back to the Maestro CLI with any requested information.

Serving UiAutomator View Hierarchy

This on-device Netty GRPC server accepts requests from their CLI tool for view hierarchy dumps. (source code) The view hierarchy is pulled from UiAutomator and the results are serialized it in an XML format. (source code)

Performing UI Interactions

UiAutomator is used on device to perform Ui Actions. (source code) However the CLI also uses adb commands to interact with the screen as well. (source code)

Waiting for Async Events

The Maestro CLI seems to do checks at a short interval when waiting for something to appear on the screen. Because the CLI is continually pulling screenshots from the device, they also do image comparisons to see when the screen has updated. (source code)

Visualizing the Implementation

Here is a wonderful Mermaid diagram that ChatGPT made me based on my post contents. Flow diagrams help me visualize this stuff, so this might be a nice way to see it as well.

flowchart TD
    A[Maestro CLI] -->|Installs Maestro APKs| B[Device with Maestro APKs]
    B -->|adb instrument command| C[Instrumentation APK]
    C -->|Starts Test| D[On-Device GRPC Server]
    D -->|Uses UiAutomator to dump View Hierarchy| E[View Hierarchy in XML]
    E -->|Sends view hierarchy data| A

    A -->|Sends interaction commands| F[ADB Commands or UiAutomator]
    F -->|Interacts with UI| G[Android App UI]

    A -->|Monitors Screen| H[Pulls Screenshots from Device]
    H -->|Performs Image Comparisons| I[Waits for Async Events]

Conclusion

Technically this is a pretty cool “hack” that is working for their product. That was fun to dive into. It is a cool strategy, so definitely try it out!

NOTE: This is just the interesting finds of my technical investigation into how it works and not a recommendation one way or another.

Want to discuss more with me? Find me on BlueSky or Threads!