<- Blog

Best Practices for Cross-platform Maestro UI Testing for Android and iOS

Using runFlow, runScript and external parameters

Ashish Kharche May 22, 2023

Suppose you are developing Android and iOS apps and want to write UI tests for both using the Maestro UI testing framework. Maestro is a versatile UI testing framework ideal for Android and iOS applications.

In this blog, we will focus on using Maestro’s key features, such as runFlow, runScript, and external parameters, to demonstrate the best practices for cross-platform testing. We will use a NASA wallpaper app built with Jetpack Compose for Android and SwiftUI for iOS as an example. However, the methodology we cover is applicable to any Maestro-supported frameworks, such as Flutter, React Native, UIKit, Android View and more!

The apps will have similar UI with minor differences, we will go through all of them as we UI test!

Prerequisites

If you want to follow along or want to try out running the test commands yourself, please download the code and run it!

Source Code: https://github.com/thekharche/maestro-nasa-wallpaper-kmm

Instructions to run the Android and iOS apps are in the README file of the GitHub code.

Let’s take a closer look at our Android and iOS apps!

Please refer to the app screenshots above when reading the flow files to have a better understanding of the UI being tested!

Setting App ID Using Environment Variables

If you need to run the same flow for apps with different app IDs, you can pass an external parameter for appId. Provide the APP_ID parameter to Maestro as follows:

maestro test -e APP_ID=com.wallpapers.androidapp .maestro/wallpapers/ListDetailWallpaper.yaml

In our case for the iOS app, we need to use use com.wallpapers.iosapp

Then, refer to it in your flow by using ${APP_ID}:

.maestro/wallpapers/ListDetailWallpaper.yaml

appId: ${APP_ID}
---
- launchApp

Handling Different Element IDs with runScript

Consider a scenario where you want to tap an image to enter the details screen, but the element IDs for the image on Android and iOS are different. For instance, Android uses wallpaperImage and iOS uses imageItem. To have one Maestro flow file test both IDs without modifying the source code, you can use the runScript feature.

Conditional runScript is available from version 1.28.0 of Maestro!

.maestro/wallpapers/ListDetailWallpaper.yaml

- runScript:
    when:
      platform: iOS
    file: ios/init.js

- runScript:
    when:
      platform: Android
    file: android/init.js

- tapOn:
    id: ${output.wallpapers.wallpaperItem}
    index: 0

.maestro/wallpapers/android/init.js

output.wallpapers = {
    wallpaperItem: 'wallpaperImage',
}

.maestro/wallpapers/ios/init.js

output.wallpapers = {
    wallpaperItem: 'imageItem',
}

The tapOn command utilizes the id from the output of the respective JavaScript file.

Conditional Execution with runFlow

In a scenario where Android does not display a date in the detail view but iOS does, you can use runFlow to conditionally test the date requirements for the iOS platform.

.maestro/wallpapers/ListDetailWallpaper.yaml

- runFlow:
    when:
      platform: iOS
    file: Date.yaml

The Date.yaml flow file will only be invoked when the platform is iOS.

.maestro/wallpapers/Date.yaml

appId: ${APP_ID}
---
- assertVisible: ".*2023-.*"

Using Inline Commands with runFlow

Assume the iOS app does not yet support setting a wallpaper image, but the Android app does. You can use runFlow with inline commands to conditionally test this feature on the Android platform.

.maestro/wallpapers/ListDetailWallpaper.yaml

- runFlow:
    when:
      platform: Android
    commands:
      - assertVisible:
          id: "setWallpaperIconButton"
      - tapOn:
            id: "setWallpaperIconButton"

Testing Platform-specific Features using runFlow

Both Android and iOS have different methods for displaying the Share drawer or bottom sheet. Using runFlow, you can test the behaviour of the share feature on both platforms.

.maestro/wallpapers/ListDetailWallpaper.yaml

- runFlow:
    when:
      platform: iOS
    file: ios/Share.yaml

- runFlow:
    when:
      platform: Android
    file: android/Share.yaml

ios/Share.yaml

appId: ${APP_ID}
---
- tapOn:
    id: "shareIconButton"
- assertVisible: ".*Copy.*"
- assertVisible: ".*Print.*"

android/Share.yaml

appId: ${APP_ID}
---
- tapOn:
    id: "shareIconButton"
- assertVisible: ".*Nearby.*"
- assertVisible: ".*Edit.*"

Complete Maestro Flow File

On GitHub: https://github.com/thekharche/maestro-nasa-wallpaper-kmm/blob/main/.maestro/wallpapers/ListDetailWallpaper.yaml

.maestro/wallpapers/ListDetailWallpaper.yaml

appId: ${APP_ID}
---

- launchApp

- runFlow: Date.yaml

- runScript:
    when:
      platform: iOS
    file: ios/init.js

- runScript:
    when:
      platform: Android
    file: android/init.js

- tapOn:
    id: ${output.wallpapers.wallpaperItem}
    index: 0

- assertVisible: ".*alien.*"

- runFlow:
    when:
      platform: iOS
    file: Date.yaml

- runFlow:
    when:
      platform: Android
    commands:
      - assertVisible:
          id: "setWallpaperIconButton"
      - tapOn:
          id: "setWallpaperIconButton"

- runFlow:
    when:
      platform: iOS
    file: ios/Share.yaml

- runFlow:
    when:
      platform: Android
    file: android/Share.yaml

Conclusion

By following these best practices, you can effectively test your apps using the Maestro UI testing framework. These cross-platform UI testing techniques ensure that your tests remain maintainable, scalable, and compatible with both Android and iOS platforms.

References

runFlow:

runFlow | Maestro maestro.mobile.dev

runScript:

runScript | Maestro maestro.mobile.dev

external parameters:

Parameters & Constants | Maestro maestro.mobile.dev

NASA Wallpaper App Source Code:

GitHub - thekharche/maestro-nasa-wallpaper-kmm: Android UI in Jetpack Compose. iOS UI in SwiftUI. Shared code in Kotlin. UI tested in Maestro. Android UI in Jetpack Compose. iOS UI in SwiftUI. Shared code in Kotlin. UI tested in Maestro. - thekharche/maestro-nasa-wallpaper-kmm github.com

🎹

We're entering a new era of software development. Advancements in AI and tooling have unlocked unprecedented speed, shifting the bottleneck from development velocity to quality control. This is why we built — a modern testing platform that ensures your team can move quickly while maintaining a high standard of quality.

Learn more ->