Automate Android app testing with UIAutomator

Photo Source
Testing is a very important process during application development. In the case of Android, application testing should be performed on a large number of devices, due to the fact that many of them have significant differences in characteristics (screen resolution, Android version, etc.). The process of manually testing an application on a large number of devices can be time-consuming, tedious, and error prone. A more efficient and reliable approach is to automate user interface testing. Using UIAutomator, you can develop a test script that will work on many Android devices
with the same accuracy and reproducibility.
UIAutomator
UIAutomator is developed by Google and ships with the Android SDK. UIAutomator is an analog of Apple’s UIAutomation tool for testing Android applications. The Android SDK provides the following tools to support automated functional testing of the user interface:
- UIAutomatorviewer - a graphical tool for recognizing user interface components in an Android application;
- UIAutomator - Java API libraries containing methods for creating user interface tests.
To use these tools, you must install the following components of the Android environment:
- Android SDK Tools, version 21 or higher;
- Android SDK platform, with API 16 or higher.
UIAutomator is constantly being updated, so the latest documentation can be found on the website: http://developer.android.com/tools/help/uiautomator/index.html .
Advantages of UIAutomator for testing applications:
- No dependence on screen resolution;
- Actions are bound to Android UI components. This allows you to work directly with user interface elements. For example, if you need to click the "OK" button, you can use the UIAutomator API to send a command to the script: click the button labeled "OK", and he clicks it. Thus, one does not have to snap to the coordinates;
- Complex sequences of user actions can be reproduced, and always this sequence will be the same;
- Tests can be run as many times as needed on various devices without the need to change Java code;
- You can use external buttons on the device (back button, turn off, volume, etc.).
But there are also disadvantages:
- It’s hard to use for applications written in HTML 5 and OpenGL, as these applications do not have Android UI elements. In this regard, it is necessary either to attach to the coordinates, or to look for alternative testing options;
- It is necessary to check, and if necessary, update Java scripts when updating Android applications.
Testing an application using UIAutomator consists of the following steps:
- Preparation for the test: installing the application on the device, analyzing its UI component;
- Creating an automated test for the application;
- Compiling the test into a JAR file and copying it to the device;
- Run the test and analyze the results;
- Correction of various errors found during testing.
Script development
To familiarize yourself with UIAutomator technologies, a simple program will be presented below that performs simple actions with the device. A standard Android application, Messaging, will be used as the tested application, and UIAutomator will send an SMS message to a specific number.
Define the actions that will be implemented in the test:
- Search and launch the application;
- Create and send a message.
As you can see, everything is quite simple.
Test preparation
UIAutomatorviewer will be used to analyze the user interface of the application. UIAutomatorviewer takes a screenshot of the device that is connected to the computer, and also provides a convenient graphical interface for displaying the hierarchy of layers and viewing the properties of each interface component individually. Having this information greatly simplifies the process of creating a UIAutomator script.

To analyze the user interface:
- Connect Android device to computer;
- Launch UIAutomatorviewer located in:
/ tools / $ uiautomatorviewer
- Click on the Device Screenshot button in the UIAutomatorviewer to capture the image from the device;
- When you select a specific interface element, its properties are displayed in the right pane. Based on these properties, an element can easily be found in a test script.
If UIAutomatorviewer cannot decompose the image into components, then the application is written using HTML 5 or OpenGL.
Setting up a development environment
If you are using Eclipse:
- Create a new Java project in Eclipse. In this project we will implement the UIAutomator script. Let's name the project: SendMessage
- We call the context menu by right-clicking on the project in Project Explorer, and select the Properties item. In Properties, find the Java Build Path and add the necessary libraries:
- To add JUnit support, select Add Library> JUnit;
- By clicking Add External JARs ... you need to go to the directory with the latest SDK in
/ platforms / and select 2 files in it: uiautomator.jar and android.jar
If using a different development environment, make sure that the UIAutomator.jar and android.jar files are added to the project settings.
UIAutomator API
To talk about all the possibilities that UIAutomator has for creating test scripts, a fairly large amount of time will be required. All detailed information can be found on the website: http://developer.android.com/tools/help/UIAutomator/index.html
Script creation
First you need to create a new file with a Java class in the SendMessage project, for example, under the name SendMessage. This class must be inherited from the UIAutomatorTestCase class. To add a library to Eclipse, just use the keyboard shortcut Ctrl + Shift + o. Other libraries are added in the same way, so I will not focus on this anymore.
We will create three functions for testing the main functionality of the application:
- Search and launch the application
- SMS sending
- Exit to the main application menu
The first function that runs all these methods, a kind of main function, will look like this:
public void test() {
// Here will be called for all other functions
}
Function to search and launch the application
The function will perform the following actions: press the Home button, to go to the main window, open the menu and find the icon with the desired application, launch this application.
Search and launch the application
private void findAndRunApp() throws UiObjectNotFoundException {
// Go to main screen
getUiDevice().pressHome();
// Find menu button
UiObject allAppsButton = new UiObject(new UiSelector()
.description("Apps"));
// Click on menu button and wait new window
allAppsButton.clickAndWaitForNewWindow();
// Find App tab
UiObject appsTab = new UiObject(new UiSelector()
.text("Apps"));
// Click on app tab
appsTab.click();
// Find scroll object (menu scroll)
UiScrollable appViews = new UiScrollable(new UiSelector()
.scrollable(true));
// Set the swiping mode to horizontal (the default is vertical)
appViews.setAsHorizontalList();
// Find Messaging application
UiObject settingsApp = appViews.getChildByText(new UiSelector()
.className("android.widget.TextView"), "Messaging");
// Open Messaging application
settingsApp.clickAndWaitForNewWindow();
// Validate that the package name is the expected one
UiObject settingsValidation = new UiObject(new UiSelector()
.packageName("com.android.mms"));
assertTrue("Unable to detect Messaging",
settingsValidation.exists());
}
All class names, button text, etc. were obtained using uiautomatorviewer.
SMS sending
In this function, we search for and press the button for creating a new message, enter the phone number, text of the message and then send it. The number and text will be passed through the function arguments:
SMS sending function
private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
// Find and click New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
newMessageButton.clickAndWaitForNewWindow();
// Find to box and enter the number into it
UiObject toBox = new UiObject(new UiSelector()
.className("android.widget.MultiAutoCompleteTextView").instance(0));
toBox.setText(toNumber);
// Find text box and enter the message into it
UiObject textBox = new UiObject(new UiSelector()
.className("android.widget.EditText").instance(0));
textBox.setText(text);
// Find send button and send message
UiObject sendButton = new UiObject(new UiSelector()
.className("android.widget.ImageButton").description("Send"));
sendButton.click();
}
We could not find the field for the number and the field for communication by any special signs, since these forms have no text or any description. Therefore, we find them using the instance method. Using this method, you can get an element by its serial number in the interface hierarchy.
We realize the possibility of receiving the phone number of the recipient as parameters, as well as the message text. It is necessary to add initialization of default parameters to the test () function, which should be overwritten with user-defined values if they were passed as arguments to the corresponding function:
Receiving parameters in the test
// Default parameters
String toNumber = "123456";
String text = "Test message";
String toParam = getParams().getString("to");
String textParam = getParams().getString("text");
if (toParam != null) {
// Remove spaces
toNumber = toParam.trim();
}
if (textParam != null) {
text = textParam.trim();
}
Thus, you can pass parameters through the command line to the script. This can be done using the –e switch. After it 2 values are transferred: parameter name and value. For example, to transfer the number "777777", as the recipient number, we pass the parameters: -e to 777777. To get these parameters in the script, use the method: getParams ().
But there are several pitfalls. For example, it is impossible to transmit text with some characters, UIAutomator does not accept them (space, &, <,>, (,), “, ', etc., as well as unicode characters). To do this, I propose to replace these characters when they are supplied to the script with some string, for example, replace the space with the string: blogspaceblog. This is convenient when we use a script to run the UIAutomator script that will process the input parameters. Add parsing to the input parameters check and replace these lines:
String replacement code
if (toParam != null) {
toNumber = toParam.trim();
}
if (textParam != null) {
textParam = textParam.replace("blogspaceblog", " ");
textParam = textParam.replace("blogamperblog", "&");
textParam = textParam.replace("bloglessblog", "<");
textParam = textParam.replace("blogmoreblog", ">");
textParam = textParam.replace("blogopenbktblog", "(");
textParam = textParam.replace("blogclosebktblog", ")");
textParam = textParam.replace("blogonequoteblog", "'");
textParam = textParam.replace("blogtwicequoteblog", "\"");
text = textParam.trim();
}
Exit to the main application menu
This function is the simplest of all that we implemented before. She presses the back button until she finds a button to create a new message.
private void exitToMainWindow() {
// Find New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
// Press back button while new message button doesn't exist
while(!newMessageButton.exists()) {
getUiDevice().pressBack();
}
}
Collection of logs from the test
In order to log test results, you can use the standard Android buffer. To work with it, you need to connect the library in the script:
import android.util.Log;
All information that is interesting can be recorded in the logs. This can be done using the function:
Log.i(String title, String title);
Logs can be read from the device using the command:
$ adb logcat
More information on logcat can be found on the official website: developer.android.com/tools/help/logcat.html
Resulting code
Thus, we have the following code:
View code
package blog.send.message;
import android.util.Log;
import com.android.UIAutomator.core.UiObject;
import com.android.UIAutomator.core.UiObjectNotFoundException;
import com.android.UIAutomator.core.UiScrollable;
import com.android.UIAutomator.core.UiSelector;
import com.android.UIAutomator.testrunner.UiAutomatorTestCase;
public class SendMessage extends UiAutomatorTestCase {
public void test() throws UiObjectNotFoundException {
// Default parameters
String toNumber = "123456";
String text = "Test message";
String toParam = getParams().getString("to");
String textParam = getParams().getString("text");
if (toParam != null) {
toNumber = toParam.trim();
}
if (textParam != null) {
textParam = textParam.replace("blogspaceblog", " ");
textParam = textParam.replace("blogamperblog", "&");
textParam = textParam.replace("bloglessblog", "<");
textParam = textParam.replace("blogmoreblog", ">");
textParam = textParam.replace("blogopenbktblog", "(");
textParam = textParam.replace("blogclosebktblog", ")");
textParam = textParam.replace("blogonequoteblog", "'");
textParam = textParam.replace("blogtwicequoteblog", "\"");
text = textParam.trim();
}
Log.i("SendMessageTest", "Start SendMessage");
findAndRunApp();
sendMessage(toNumber, text);
exitToMainWindow();
Log.i("SendMessageTest", "End SendMessage");
}
// Here will be called for all other functions
private void findAndRunApp() throws UiObjectNotFoundException {
// Go to main screen
getUiDevice().pressHome();
// Find menu button
UiObject allAppsButton = new UiObject(new UiSelector()
.description("Apps"));
// Click on menu button and wait new window
allAppsButton.clickAndWaitForNewWindow();
// Find App tab
UiObject appsTab = new UiObject(new UiSelector()
.text("Apps"));
// Click on app tab
appsTab.click();
// Find scroll object (menu scroll)
UiScrollable appViews = new UiScrollable(new UiSelector()
.scrollable(true));
// Set the swiping mode to horizontal (the default is vertical)
appViews.setAsHorizontalList();
// Find Messaging application
UiObject settingsApp = appViews.getChildByText(new UiSelector()
.className("android.widget.TextView"), "Messaging");
// Open Messaging application
settingsApp.clickAndWaitForNewWindow();
// Validate that the package name is the expected one
UiObject settingsValidation = new UiObject(new UiSelector()
.packageName("com.android.mms"));
assertTrue("Unable to detect Messaging",
settingsValidation.exists());
}
private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
// Find and click New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
newMessageButton.clickAndWaitForNewWindow();
// Find to box and enter the number into it
UiObject toBox = new UiObject(new UiSelector()
.className("android.widget.MultiAutoCompleteTextView").instance(0));
toBox.setText(toNumber);
// Find text box and enter the message into it
UiObject textBox = new UiObject(new UiSelector()
.className("android.widget.EditText").instance(0));
textBox.setText(text);
// Find send button and send message
UiObject sendButton = new UiObject(new UiSelector()
.className("android.widget.ImageButton").description("Send"));
sendButton.click();
}
private void exitToMainWindow() {
// Find New message button
UiObject newMessageButton = new UiObject(new UiSelector()
.className("android.widget.TextView").description("New message"));
// Press back button while new message button doesn't exist
while(!newMessageButton.exists()) {
getUiDevice().pressBack();
sleep(500);
}
}
}
Compiling and running the UIAutomator test
- To generate test build configuration files, you must run the following command in the terminal:
Where$
/tools/android create uitest-project -n -t -p - имя проекта, который создавался для теста UIAutomator (в нашем случае: SendMessage),
- выбор устройства и Android API Level (можно получить список установленных устройств командой: /tools/android list targets) и - путь к директории с проектом.
Необходимо экспортировать переменную окружения ANDROID_HOME:- Windows:
set ANDROID_HOME=
- Unix:
export ANDROID_HOME=
Заходим в директорию с проектом, в которой лежит сгенерированный на шаге 1 файл build.xml и выполняем команду:$ ant build
Копируем собранный JAR файл на устройство с помощью команды adb push:
Для нашего случая:$ adb push
/data/local/tmp/ $ adb push
/bin/SendMessage.jar /data/local/tmp/
Запускаем скрипт:$ adb shell uiautomator runtest /data/local/tmp/SendMessage.jar –c blog.send.message.SendMessage -e to 777777
Conclusion
Using UIAutomator is very convenient for high-quality testing of applications on a large number of devices. Using this technology, you can create various Demos to demonstrate the application. - Windows: