Automatic Android* Testing with UiAutomator

来源:互联网 发布:网络教育学士学位证 编辑:程序博客网 时间:2024/05/16 06:35

Automatic Android* Testing with UiAutomator

I want to tell you about a wonderful tool to automatically test the UI of Android* applications. The name of this tool is UiAutomator. You can find all the latest documentation here: http://developer.android.com/tools/help/uiautomator/index.html and http://developer.android.com/tools/testing/testing_ui.html

UiAutomator has some strengths and weaknesses.

Advantages:

  • Can be used on device displays with different resolution
  • Events can be linked with Android UI controls. For example, click on a button with text that says “Ok,” instead of clicking a coordinate location (x=450, y=550).
  • Can reproduce a complex sequence of user actions
  • Always performs the same sequence of actions, allowing us to collect performance metrics on different devices.
  • Can run several times and on different devices without changing any Java* code
  • Can use hardware buttons on devices

Disadvantages:

  • Hard to use with OpenGL* and HTML5 applications because these apps have no Android UI components.
  • Time consuming to write JavaScript*

Script development

To introduce working with UiAutomator, I want to show a simple program. This program is the standard Android Messaging application and sends SMS messages to any phone number.

Here's a short description of the actions that we implemented:

  1. Find and run application
  2. Create and send SMS messages

As you can see it’s very easy.

Preparation for the test

To analyze the UI interface we will use uiautomatorviewer.

uiautomatorviewer shows a split screenshot of all the UI components in the Node Detail so you can see their different properties. From the properties you can find a desired element.

Customizing the Development Environment

If you use Eclipse*:

  1. Create a new Java project in Eclipse. We will call our project: SendMessage
  2. Right click on your project in the Project Explorer and click on the Properties item
  3. In Properties, select Java Build Path and add the required libraries:
  • Click on the Add Library JUnit and there JUnit3 choose to add support for JUnit
  • Click Add External JARs ...
  • In the <android-sdk>/platforms/directory, select the latest version of the SDK. Also in this directory, select the files: uiautomator.jar and android.jar

If you are using a different development environment, make sure that the android.jar and uiautomator.jar files are added to the project settings.

Create a script

Create a project in a previously created new file with the Java class. Call it SendMessage. This class is inherited from class UiAutomatorTestCase and using keys Ctrl + Shift + o (for Eclipse), add the required libraries.

Create three functions to test this application:

  1. Search and run the application
  2. Send SMS messages
  3. Exit to the main menu of the application

Create a function from which we will run all these features—a kind of main function:

1public void test() {
2    // Here will be called for all other functions
3    }

Function to find and run the application

This function is simple. We press the Home button and once on the main window, open the menu and look for the icon with the application. Click on it and start the application.

01private void findAndRunApp() throws UiObjectNotFoundException {
02        // Go to main screen
03        getUiDevice().pressHome();
04        // Find menu button
05        UiObject allAppsButton = new UiObject(new UiSelector()
06        .description("Apps"));
07        // Click on menu button and wait new window
08        allAppsButton.clickAndWaitForNewWindow();
09        // Find App tab
10        UiObject appsTab = new UiObject(new UiSelector()
11        .text("Apps"));
12        // Click on app tab
13        appsTab.click();
14        // Find scroll object (menu scroll)
15        UiScrollable appViews = new UiScrollable(new UiSelector()
16        .scrollable(true));
17        // Set the swiping mode to horizontal (the default is vertical)
18        appViews.setAsHorizontalList();
19        // Find Messaging application
20        UiObject settingsApp = appViews.getChildByText(new UiSelector()
21        .className("android.widget.TextView"), "Messaging");
22        // Open Messaging application
23        settingsApp.clickAndWaitForNewWindow();
24         
25        // Validate that the package name is the expected one
26        UiObject settingsValidation = new UiObject(new UiSelector()
27        .packageName("com.android.mms"));
28        assertTrue("Unable to detect Messaging",
29                settingsValidation.exists());
30    }

All of the class names, the text on the buttons, etc. came from uiautomatorviewer.

Send SMS messages

This function finds and presses the button for writing a new application, enters a phone number for whom to send a text message to, and presses the send button. The phone number and text pass through the function arguments:

01private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
02        // Find and click New message button
03        UiObject newMessageButton = new UiObject(new UiSelector()
04        .className("android.widget.TextView").description("New message"));
05        newMessageButton.clickAndWaitForNewWindow();
06         
07        // Find to box and enter the number into it
08        UiObject toBox = new UiObject(new UiSelector()
09        .className("android.widget.MultiAutoCompleteTextView").instance(0));
10        toBox.setText(toNumber);
11        // Find text box and enter the message into it
12        UiObject textBox = new UiObject(new UiSelector()
13        .className("android.widget.EditText").instance(0));
14        textBox.setText(text);
15         
16        // Find send button and send message
17        UiObject sendButton = new UiObject(new UiSelector()
18        .className("android.widget.ImageButton").description("Send"));
19        sendButton.click();
20    }

The fields for the phone number and text message display do not have any special features, as neither the text nor any description of these fields is available. Therefore, I can find them by using in this instance an element in its ordinal position in the hierarchy of the interface.

To add the ability to pass parameters to the script we can specify the number of where we want to send the message, as well as text messages. The function test () initializes the default settings, and if any of the parameters were sent messages via the command line, the substitution of the default settings would be:

01// Default parameters
02        String toNumber = "123456";
03        String text = "Test message";
04         
05        String toParam = getParams().getString("to");
06        String textParam = getParams().getString("text");
07if (toParam != null) {
08// Remove spaces
09            toNumber = toParam.trim();
10        }
11        if (textParam != null) {
12            text = textParam.trim();
13        }

Thus we will be able to pass parameters from the command line in the script using the small key -e, the first name of the parameter, and the second value. For example, my application sends the number to send " 777777 »:-e to 777777

There are some pitfalls. For example, this application doesn’t understand some characters and fails. It is impossible to convey just text with some characters , it does not perceive them and fails. Here are some of them: space, &, <, > , (,) , ", ' , as well as some Unicode characters. I replace these characters when applying them to script a string, such as a space line : blogspaceblog. So when the script starts UiAutomator, we use a script that will handle our input parameters. We add the function test (), where we check whether there are options, parse parameters, and replace them with real characters. Here is a sample code along with demonstrating what we inserted earlier:

01if (toParam != null) {
02            toParam = toParam.replace("blogspaceblog", " ");
03            toParam = toParam.replace("blogamperblog", "&");
04            toParam = toParam.replace("bloglessblog", "<");
05            toParam = toParam.replace("blogmoreblog", ">");
06            toParam = toParam.replace("blogopenbktblog", "(");
07            toParam = toParam.replace("blogclosebktblog", ")");
08            toParam = toParam.replace("blogonequoteblog", "'");
09            toParam = toParam.replace("blogtwicequoteblog", """);
10            toNumber = toParam.trim();
11        }
12        if (textParam != null) {
13            textParam = textParam.replace("blogspaceblog", " ");
14            textParam = textParam.replace("blogamperblog", "&");
15            textParam = textParam.replace("bloglessblog", "<");
16            textParam = textParam.replace("blogmoreblog", ">");
17            textParam = textParam.replace("blogopenbktblog", "(");
18            textParam = textParam.replace("blogclosebktblog", ")");
19            textParam = textParam.replace("blogonequoteblog", "'");
20            textParam = textParam.replace("blogtwicequoteblog", """);
21            text = textParam.trim();
22        }

Exit to the main menu of the application

This function is the simplest of all those that we have implemented. I simply press a button in a loop back until it shows the button to create a new message.

01private void exitToMainWindow() {
02        // Find New message button
03        UiObject newMessageButton = new UiObject(new UiSelector()
04        .className("android.widget.TextView").description("New message"));
05         
06        // Press back button while new message button doesn't exist
07        while(!newMessageButton.exists()) {
08            getUiDevice().pressBack();
09        }
10    }

Source code

So here is our code:

001package blog.send.message;
002import com.android.uiautomator.core.UiObject;
003import com.android.uiautomator.core.UiObjectNotFoundException;
004import com.android.uiautomator.core.UiScrollable;
005import com.android.uiautomator.core.UiSelector;
006import com.android.uiautomator.testrunner.UiAutomatorTestCase;
007 
008public class SendMessage extends UiAutomatorTestCase {
009    public void test() throws UiObjectNotFoundException {
010        // Default parameters
011        String toNumber = "123456";
012        String text = "Test message";
013         
014        String toParam = getParams().getString("to");
015        String textParam = getParams().getString("text");
016        if (toParam != null) {
017            toParam = toParam.replace("blogspaceblog", " ");
018            toParam = toParam.replace("blogamperblog", "&");
019            toParam = toParam.replace("bloglessblog", "<");
020            toParam = toParam.replace("blogmoreblog", ">");
021            toParam = toParam.replace("blogopenbktblog", "(");
022            toParam = toParam.replace("blogclosebktblog", ")");
023            toParam = toParam.replace("blogonequoteblog", "'");
024            toParam = toParam.replace("blogtwicequoteblog", """);
025            toNumber = toParam.trim();
026        }
027        if (textParam != null) {
028            textParam = textParam.replace("blogspaceblog", " ");
029            textParam = textParam.replace("blogamperblog", "&");
030            textParam = textParam.replace("bloglessblog", "<");
031            textParam = textParam.replace("blogmoreblog", ">");
032            textParam = textParam.replace("blogopenbktblog", "(");
033            textParam = textParam.replace("blogclosebktblog", ")");
034            textParam = textParam.replace("blogonequoteblog", "'");
035            textParam = textParam.replace("blogtwicequoteblog", """);
036            text = textParam.trim();
037        }
038        findAndRunApp();
039            sendMessage(toNumber, text);
040            exitToMainWindow();
041    }
042    // Here will be called for all other functions
043    private void findAndRunApp() throws UiObjectNotFoundException {
044        // Go to main screen
045        getUiDevice().pressHome();
046        // Find menu button
047        UiObject allAppsButton = new UiObject(new UiSelector()
048        .description("Apps"));
049        // Click on menu button and wait new window
050        allAppsButton.clickAndWaitForNewWindow();
051        // Find App tab
052        UiObject appsTab = new UiObject(new UiSelector()
053        .text("Apps"));
054        // Click on app tab
055        appsTab.click();
056        // Find scroll object (menu scroll)
057        UiScrollable appViews = new UiScrollable(new UiSelector()
058        .scrollable(true));
059        // Set the swiping mode to horizontal (the default is vertical)
060        appViews.setAsHorizontalList();
061        // Find Messaging application
062        UiObject settingsApp = appViews.getChildByText(new UiSelector()
063        .className("android.widget.TextView"), "Messaging");
064        // Open Messaging application
065        settingsApp.clickAndWaitForNewWindow();
066         
067        // Validate that the package name is the expected one
068        UiObject settingsValidation = new UiObject(new UiSelector()
069        .packageName("com.android.mms"));
070        assertTrue("Unable to detect Messaging",
071                settingsValidation.exists());
072    }
073     
074    private void sendMessage(String toNumber, String text) throws UiObjectNotFoundException {
075        // Find and click New message button
076        UiObject newMessageButton = new UiObject(new UiSelector()
077        .className("android.widget.TextView").description("New message"));
078        newMessageButton.clickAndWaitForNewWindow();
079         
080        // Find to box and enter the number into it
081        UiObject toBox = new UiObject(new UiSelector()
082        .className("android.widget.MultiAutoCompleteTextView").instance(0));
083        toBox.setText(toNumber);
084        // Find text box and enter the message into it
085        UiObject textBox = new UiObject(new UiSelector()
086        .className("android.widget.EditText").instance(0));
087        textBox.setText(text);
088         
089        // Find send button and send message
090        UiObject sendButton = new UiObject(new UiSelector()
091        .className("android.widget.ImageButton").description("Send"));
092        sendButton.click();
093    }
094     
095    private void exitToMainWindow() {
096        // Find New message button
097        UiObject newMessageButton = new UiObject(new UiSelector()
098        .className("android.widget.TextView").description("New message"));
099         
100        // Press back button while new message button doesn't exist
101        while(!newMessageButton.exists()) {
102            getUiDevice().pressBack();
103            sleep(500);
104        }
105    }
106}

Compile and run the test UiAutomator

  1. To generate configuration files for test assembly, run the following command from the command line:
    <android-sdk>/tools/android create uitest-project -n <name> -t  <target-id> -p <path>
    Where <name> is the name of the project that was created to test UiAutomator (in our case: SendMessage), <target-id> is the choice of device and Android API Level (you can get a list of installed devices, the team (<android-sdk> / tools / android list targets), and <path> is the path to the directory that contains the project.
  2. You must export the environment variable ANDROID_HOME:
    • On Windows*:
      set ANDROID_HOME=<path_to_your_sdk>
    • On UNIX*:
      export ANDROID_HOME=<path_to_your_sdk>
  3. Go to the directory with the project file, build.xml, which was generated in step 1, and run the command:
    ant build
  4. Copy the compiled JAR file to the device using the adb push:
    adb push <path_to_output_jar> /data/local/tmp/
    In our case, it is:
    adb push <project_dir>/bin/SendMessage.jar /data/local/tmp/
  5. And run the script:
    adb shell uiautomator runtest /data/local/tmp/SendMessage.jar –c blog.send.message.SendMessage

About the Author

Egor Churaev (egor.churaev@intel.com) – Software Intern

Related Articles and Resources:

  • HTML5 - Intel Developer Zone
  • Android - Intel Developer Zone
For more complete information about compiler optimizations, see our Optimization Notice.
原创粉丝点击