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:

    1. Preparation for the test: installing the application on the device, analyzing its UI component;
    2. Creating an automated test for the application;
    3. Compiling the test into a JAR file and copying it to the device;
    4. Run the test and analyze the results;
    5. 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:

    1. Search and launch the application;
    2. 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.

    image

    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:
      1. To add JUnit support, select Add Library> JUnit;
      2. 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:

    1. Search and launch the application
    2. SMS sending
    3. 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


    1. To generate test build configuration files, you must run the following command in the terminal:
      $ /tools/android create uitest-project -n  -t   -p 
      Where - имя проекта, который создавался для теста 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.

    Also popular now: