UI tests with Jetpack Compose and Appium x UIAutomator

Julien Salvi
3 min readDec 5, 2022

At Aircall we are pushing a lot around quality thanks to our great QA team. Huge efforts are put on Android developer side with the unit tests and our QA engineers tackle everything around automation & UI tests.

Before integrating our brand new design system made with Compose UI in production, we wanted to be sure that the transition to Compose would be painless with our automation framework and tools: Appium and UIAutomator.

This article will explain how the Android team validated the move to Compose with our automation framework.

Automation tests with View components

The Aircall QA team has developed lots of automation tests that cover more than 90% of the use cases in the Android app. And to do so, they rely on Appium with the UI testing framework UIAutomator which is an instrumentation-based API and works with the AndroidJUnitRunner test runner.

To locate a View-based component in the application, UIAutomator provides an annotation @AndroidFindBy where you need to specify the resource id of a UI element you want to identify.

@AndroidFindBy(id = "myButtonId")
protected WebElement myButton;

But this method does work properly with Compose UI components. Indeed, when looking for a Composable with its dedicated tag, no elements are found in the screen.

Now, let’s have a closer look at the changes we had to make in order to smoothly add our new Compose code in the current codebase and not breaking everything on the automation side.

UI tests with Compose UI and UIAutomator

To unlock the UI testing with UIAutomator and Compose, we will have to take advantage of the semantics Modifier in each Composable we want to locate with the testTag property.

In order to ease the migration, we are going to keep the same component IDs we were using with our View-based elements.

Modifier.semantics {
testTag = "myButtonId"
}

And at the top level Composable, we should toggle the configuration to map testTags to the resource IDs in the semantics Modifier. This will allow the automation framework to find the UI components from a given test tag.

Modifier.semantics { testTagsAsResourceId = true }

Now that the Composable can be identified in the app, we must update the way elements are located with UIAutomator on the automation framework side. Instead of directly finding the component with its resource ID, the UiSelector class with the component ID will be used in the @AndroidFindBy annotation to locate the Composable.

@AndroidFindBy(uiAutomator = "new UiSelector().resourceId(\"myButtonId\")")
protected WebElement myButton;

There are several ways to use UiSelector to discover elements in a screen. We can find a UI component (Compose or View) thanks to its resource ID, text or class name. If you want to know more about the locator strategy with Appium UIAutomator, you can have a look the dedicated documentation.

UiSelector().resourceId("resourceid");
UiSelector().text("text");
UiSelector().className("classname");

We are now all set with Compose and our automation tools, and the application is ready to be tested! Here is a sneak peek of the automation framework with a sample app that showcase our Compose design system.

Locating Compose UI with our automation framework

We can now locate our Composable and interact with them easily from the automation framework. Thanks to that, the migration will be handled very smoothly without important rewrites on the QA side.

Exploring this crucial aspect has validated our move to Jetpack Compose and we are now very confident in moving forward and use our brand new design system in production without a huge migration effort on the QA side.

I’d like to give a big shout out to Hernan Mateika who has been so helpful during the investigation and his great work on the automation framework. A key player 👏

Do not hesitate to ping me on Twitter if you have any question 🤓

--

--

Julien Salvi

Google Developer Expert for Android — Lead Android Engineer @ Aircall (Paris) — Startup way of life, beer lover and world traveler.