(1/2) E2E Testing Android Apps With ADB

Phil Blenkinsop
Econify
Published in
6 min readDec 8, 2023

--

Testing your main user journeys to find any issues before they reach your end users is a critical requirement of any release cycle. These can include sign in and sign up flows or purchasing a product. Automating these journeys reduces the burden of manual testing on your team, increases the repeatability and minimises the risk of human error.

In order to test user journeys, you need to run the app on a device. To avoid the overhead of managing a fleet of real devices to test on, the Android Emulator provides access to a range of virtual devices which can be run locally as well as in CI pipelines.

ADB (Android Debug Bridge) is a great utility for communicating with the emulated device and allows installing APKs and simulating user input such as typing and screen presses.

Using both of these tools in tandem creates a platform to run android apps and simulate user journeys in a repeatable and automated manner. Running your tests in this way is considered black box testing. In my opinion this is the best way to run your E2E tests, as it means the test interacts with the app in exactly the same way a user would.

This guide will walk through the process of automating a Sign In journey using ADB for the Zoom app.

Zoom app welcome screen

Table of Contents:

  1. Installing and running the app
  2. Simulating user input
  3. Example sign in test
  4. Wrap up and next steps

— — —

If you want to skip straight to the sample repository there is a link at the bottom!

  1. Installing and running the app

(This guide assumes you already have the Android Emulator configured and a virtual device created. Setup steps can be found for the Emulator here if needed. This guide was created using a Nexus 5X using Android 13 Playstore SDK system-images;android-33;google_apis_playstore;arm64-v8a )

Installing your app is a simple process thanks to adb install . Simply provide the path to the APK file(s) saved locally. In our case, the Zoom app utilises split APKs so we need to use adb install-multiple to install all parts of the split APK. We pass the path of the folder where the APK files are installed, and add a wilcard to pick up all of the files in one install command:

adb install-multiple ./zoom-apk/*.apk

Normally to run an app on a phone, you’d find it by scrolling around on the screen and tapping on the icon to open it. This wouldn’t be an ideal approach for automated tests as you can’t guarantee the screen’s state at any given moment which could result in flakey tests. Therefore we need to be able to open the app using ADB commands.

We can use adb shell and the Android Activity manager to start the app with adb shell am start , which starts an activity on the device. In order to know what activity to start, we need find the ‘Launcher’ activity of the Zoom app which can be done using adb shell cmd package resolve-activity . Combining both together, we can launch the app in a single command where us.zoom.videomeetings is the package name of the app.

adb shell am start "us.zoom.videomeetings/$(adb shell cmd package resolve-activity -c android.intent.category.LAUNCHER us.zoom.videomeetings | sed -n '/name=/s/^.*name=//p')"

Launching Zoom app with one CLI command

2. Simulating user input

Using adb shell input there are a few options for simulating user input including text and touching coordinates on screen:

  • adb shell input tap x y (where x & y are coordinates on the screen)
  • adb shell input type "z” (where z is a word or phrase)

In order to execute these reliably, we first need to find the elements on screen we want to target.

For example, to press a button on screen we ideally want to find it by text as a user would with their eyes, and then find it’s coordinates on screen to tap. This is a preferable method over hardcoding fixed coordinates so that the test doesn’t break when the layout changes on different screen sizes.

To find an element, we can use the Android UI Automator together with adb exec-out which executes a command on the device and returns the output to our stdout. The UI Automater provides a dump command which will return an XML tree of all the elements on screen. This normally outputs to a file, so instead dumping to/dev/tty will return it to our stdout .

adb exec-out uiautomator dump /dev/tty

This also includes a string to say the UI was dumped to our requested location. We can pipe the output into awk to remove it, so that we are left with valid XML to crawl.

adb exec-out uiautomator dump /dev/tty | awk '{gsub("UI hierchary dumped to: /dev/tty", "");print}'

We can then use a utility such as xmllint to crawl the outputted XML and find elements. Another option is converting the XML to JSON and crawling that, which is what I do in the sample repo (link at bottom).

We want to find elements by their text as a user would, so we have to search the XML elements where the node’s text property is what we are looking for, in this case the ‘Sign In’ button.

adb exec-out uiautomator dump /dev/tty | awk '{gsub("UI hierchary dumped to: /dev/tty", "");print}' | xmllint --xpath '//*[@text="Sign In"]' -

From the above command, we get the following XML node:

<node index="1" text="Sign In" resource-id="us.zoom.videomeetings:id/btnLogin" class="android.widget.Button" package="us.zoom.videomeetings" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[63,1626][1017,1752]"/>

Each of the nodes have a bounds property in the format bounds=[minX, minY][maxX, maxY] . Using these values we can calculate the centre of the button. In the above case it would be

bounds="[63,1626][1017,1752]"

centerXCoordinate -> (63 + 1017) / 2 = 540
centerYCoordinate -> (1626 + 1752) / 2 = 1689

Then using the adb shell input tap 540 1689 , we can press the centre of that button. Similarly for text inputs, we can find and tap it in the same way, and then once it’s focused we can type an email address adb shell input type example@email.com.

3. Example sign in test

Combining the commands from sections 1 and 2, we can create a automated test to install and sign in to the Zoom app in a reliable manner, with an assertion to ensure we are signed in and directed to the home screen.

The below code is written in Javascript and uses some simple utility functions which can be found in the sample repo link at the bottom of the article. They provide some basic abstraction and retry-ability of the adb commands described in sections 1 & 2.

import { assertScreenContains, clickOnElement, execCommand } from "./util.mjs";

const ZOOM_PACKAGE_NAME = "us.zoom.videomeetings";
const EMAIL = "" // add your zoom email here
const PASSWORD = "" // add your zoom password here

// Assumes emulator is already running

console.log("Installing APK...");
await execCommand("adb install-multiple ./zoom-apk/*.apk");
console.log("APK Installed!");
console.log("Launching APK...");
await execCommand(
`adb shell am start "${ZOOM_PACKAGE_NAME}/$(adb shell cmd package resolve-activity -c android.intent.category.LAUNCHER ${ZOOM_PACKAGE_NAME} | sed -n '/name=/s/^.*name=//p')"`
);
console.log("APK Launched!");

await clickOnElement("Sign In")
await clickOnElement("Email")
await execCommand(`adb shell input text "${EMAIL}"`)
await clickOnElement("Password")
await execCommand(`adb shell input text "${PASSWORD}"`)
await clickOnElement("Sign In")

await assertScreenContains(`Meetings`)

console.log("Uninstalling APK...");
await execCommand(`adb uninstall ${ZOOM_PACKAGE_NAME}`);
console.log("APK Uninstalled!");

Running the above script results in an automated test that starts the Zoom app, signs in with a username and password and then asserts the content on the home screen is correct.

Testing the Zoom app Sign In

4. Wrap up and next steps

Running these tests locally is the easy part, but being able to run them during CI or in the cloud without manual intervention is where the real value will be gained.

For CI, the script can be easily reworked to fit in a Bitrise pipeline which allows you to run tests against an Android emulator. Similarly, CircleCI also supports android emulators, and ADB can be configured to communicate with a device anywhere through adb connect .

Another possibility is using Genymotion virtual devices which provide a virtual android device in the cloud on a pay per use basis. Feel free to read the second part of this series detailing how to run the tests against a Genymotion Device Image instance in AWS!

Read part two here!

Thanks for reading and please check out the sample repo for the whole code including the utility functions, which can be found in the part-one-local-emulator folder.

https://github.com/blenky36/adb-automated-testing

--

--