How to Speed Up Your Maestro Tests: A Practical Guide

A practical guide to finding the bottlenecks in your Maestro suite and making test runs faster without sacrificing coverage or reliability.

Dan Caseley
Maestro Architect

So, you’ve got yourself a suite of tests, they’re running and they’re useful. You’re able to ship with confidence. The only problem is that you’re waiting longer than you’d like for results.

Let’s assume that you’ve already ruled out things like a non-performant application, or a slow test environment. You’ve just got enough tests that all of those taps and swipes are taking too long, and you want your feedback sooner.

Six changes get you there: disable animations, scroll faster, navigate with deep links, prefill login details, skip the login screen, and run your flows in parallel on Maestro Cloud.

Disable animations

There’s a bunch of Maestro steps that end with checking for stable state before proceeding to the next step. Animations can slow those steps right down.

If you’re using Maestro Cloud, animations are disabled by default, but you can make it explicit (docs).

platform:
  ios:
    disableAnimations: true

If you’re doing things locally, you can do similar.

Android:

adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0

iOS:

xcrun simctl spawn booted defaults write com.apple.Accessibility ReduceMotionEnabled -bool true

These settings won’t disable all animations. If you’ve got Lottie on iOS, for example, the ReduceMotionEnabled setting won’t alter anything. At that point, you need to disable it yourself. Your best option is launchArguments:

- launchApp:
    arguments:
      isE2E: true

You’ll need something in your app to receive these (docs): launch arguments on iOS, or intent extras on Android. With this, you introduce testability into your application.

Scroll faster

scrollUntilVisible keeps scrolling until it finds the element (docs), and you can increase speed to make each scroll faster. Where you know the target will be at the bottom of the list, you can avoid the risk of “overshooting” and set the speed higher.

- scrollUntilVisible:
    element:
      text: "Order #1042"
    direction: DOWN
    speed: 80

The speed ranges from 0 to 100 and defaults to 40. Beware: 100 can be so fast that an Android emulator may register a tap instead!

Think about how much of each flow is spent just getting to the screen you actually want to test. Tap the tab bar, open a menu, pick a category, open an item… Lots of your execution time is taken up with “getting ready to test” rather than “testing”. And there’ll be repetitions of those same navigation steps across your test suite. Multiply that out and it adds up fast.

Instead, use a deep link to jump straight to the subject of the test (docs), then assert that you’ve landed in the right place:

- openLink: myapp://orders/1042
- assertVisible: "Order #1042"

Universal links (App Links, on Android) work just as well. If your app already opens https:// links to your own site, you can use those in exactly the same way:

- openLink: https://example.com/orders/1042
- assertVisible: "Order #1042"

Your app needs to handle links, of course, but you might already have them for marketing purposes. And keep a few flows that do walk the real navigation path, so you know it still works. Everywhere else, skip straight to the bit you actually care about.

Prefill login details

If your app has authentication, then nearly every flow starts by logging in. That means tapping into the email field, typing it out character by character, doing the same for the password, and then signing in. inputText is one of the slower things a flow can do, and you’re paying for it at the start of every single test. Nobody needs 100 tests of the login page.

Deep links can help here too. Add a deep link to your login screen that accepts the email and password, and have your app pre-fill the input boxes from it:

appId: com.example.shop
env:
  EMAIL: test.user@example.com
  PASSWORD: hunter2
---
- launchApp
- openLink: myapp://login?email=${EMAIL}&password=${PASSWORD}  # Beware URL escaping for complex passwords
- tapOn: "Sign in"
- assertVisible: "Your orders"

That small piece of testability code in your app could save you around ten seconds per flow, on every flow. This isn’t a good security pattern, so keep this to your test builds. Don’t forget to keep one flow that fills in the login form for real so that it stays covered!

Skip the login screen

Most tests don’t need to check that sign-in works; they just need a signed-in user. You could just skip the login screen entirely. Get a token straight from your auth API, then hand it to the app at launch.

Maestro can make HTTP calls from JavaScript with runScript:

// get-token.js
const response = http.post('https://api.example.com/auth/login', {
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: EMAIL, password: PASSWORD })
})
output.token = json(response.body).token

Then pass the token to your app as a launch argument, the same mechanism we used to switch off animations:

appId: com.example.shop
env:
  EMAIL: test.user@example.com
  PASSWORD: TestPassword123
---
- runScript: get-token.js
- launchApp:
    arguments:
      authToken: ${output.token}
- assertVisible: "Your orders"

Your app reads authToken on launch, stores it as if the user had just logged in, and opens on the signed-in screen. An HTTP call takes a fraction of a second, far faster than going through the UI and logging in through form inputs.

As before, don’t forget to keep at least one flow that signs in through the real login screen.

Parallelize the suite

Everything so far makes each flow faster. But run your suite locally and those flows still run one after another on a single device, so the total wait is the sum of every flow in the suite.

Maestro Cloud has many devices. Upload your whole suite, and Maestro Cloud runs it on our infrastructure in parallel:

maestro cloud --app-file app.apk --flows .maestro/

That gives you a multiplier on top of all the savings above. A suite of 40 flows at a minute and a half each takes an hour back to back. Spread across 10 Maestro Cloud devices, it’s done in about six minutes.

To get the most out of it, keep your flows independent and idempotent. Each one should be able to run on a clean device with its own test data, without relying on another flow’s state. Run your suite on Maestro Cloud and stop waiting on your tests.

Conclusion

The biggest wins come from removing repeated work: disable animations, speed up scrolling, deep-link past navigation you are not testing, and avoid unnecessary sign-in steps. Keep the journeys you care about covered, then parallelize independent flows in Maestro Cloud to get results sooner.

Share

Frequently asked questions

Does Maestro Cloud disable animations?

Yes, by default. You can also set it explicitly with `disableAnimations: true` under `platform.ios` in your workspace configuration.

How fast can scrollUntilVisible scroll?

The `speed` setting goes from 0 to 100 and defaults to 40. At 100 it can be so fast that an Android emulator registers a tap.

How do I skip the login screen in Maestro tests?

Get a token from your auth API with an HTTP call in `runScript`, then pass it to the app as a launch argument. Your app reads the token on launch and opens signed in. Keep at least one flow that signs in through the real login screen.

Can Maestro run flows in parallel?

Yes, on Maestro Cloud. Upload your suite with `maestro cloud` and the flows run in parallel across devices. A suite of 40 flows at 90 seconds each takes about 6 minutes on 10 devices, compared with an hour back to back.

Get the next one in your inbox

Pick what you want to hear about. Unsubscribe any time.

What to send you