UI Test under Android (UIAutomator/Espresso)

Android also offers a way to run UI tests. Several libraries are available but the latest officially supported libraries seem to be Espresso and UIAutomator. UIAutomator is newer but requires API Level 14. You can find a short description here: http://developer.android.com/training/testing/ui-testing/uiautomator-testing.html Espresso needs only API level 8 but is more limited and cannot create screenshots … Continue reading “UI Test under Android (UIAutomator/Espresso)”

Android also offers a way to run UI tests. Several libraries are available but the latest officially supported libraries seem to be Espresso and UIAutomator. UIAutomator is newer but requires API Level 14. You can find a short description here:

http://developer.android.com/training/testing/ui-testing/uiautomator-testing.html

Espresso needs only API level 8 but is more limited and cannot create screenshots on its own:

http://developer.android.com/training/testing/ui-testing/espresso-testing.html

While they are similar to the iOS UI tests they seem to be much more hardware dependent. E.g. where iOS can also find and tap table rows that are outside the screen (which makes it easier to support different screen sizes for the tests) Android “sees” only what is on the screen, making it necessary to add swipe gestures, which makes the tests dependent on a certain screen size.

To find out how to access a certain screen element you can use the “uiautomatorviewer” program.

Automatically starting the app on multiple devices

If your app is compatible with older Android versions but you are also using new features, you have to take care not to load the classes that are only available on newer Android versions on old Android versions, otherwise your app will crash. So it is also useful to run the tests on multiple emulators with different Android versions. For this purpose you can add some lines to your build.gradle file:

task testemulator << {
  emulatorstart("Test16")
  emulatorstart("Test23")
  emulatorstart("Test40")
}

def emulatorstart(emulatorname) {
  Process process = ("emulator -avd " + emulatorname + " -gpu off").execute()
  Thread.sleep(60000)
  "adb -e shell input keyevent 82".execute().waitFor() // unlock screen
  "adb -e uninstall com.example.app".execute().waitFor()
  "adb -e install build/outputs/apk/example.apk".execute().waitFor()
  "adb -e shell am start -n com.example.app/com.example.app.Main".execute().waitFor()
  Thread.sleep(10000)

  // Here you could run your tests. In this example a screenshot is saved.
  "adb shell screencap -p /sdcard/screen.png".execute().waitFor()
  ("adb pull /sdcard/screen.png " + emulatorname + ".png").execute().waitFor()
  "adb shell rm /sdcard/screen.png".execute().waitFor()
  process.waitForOrKill(10000)
}

By running

gradle testemulator

your app will be installed on all specified emulators (e.g. "Test16", "Test23" and "Test40") and started. Afterward a screenshot with the name of the emulator will be created. This way you can run it (which takes a while) and then check the screenshots to see if it was successfully started on all these emulators and Android versions.

Screenshots

Screenshots on newer Android devices can be created in the way described above. However e.g. Android 2.x does not have the "screencap" program installed. If you would like to create screenshots with older Android versions or if "screencap" does not work, you can use a Java program that you can find here. To integrate it into the gradle script above just replace the three lines

"adb shell screencap -p /sdcard/screen.png".execute().waitFor()
("adb pull /sdcard/screen.png " + emulatorname + ".png").execute().waitFor()
"adb shell rm /sdcard/screen.png".execute().waitFor()

with

screenshot(emulatorname);

and add the following function to your gradle file:

def screenshot(emulatorname) {
    exec {
        executable "java"
        args "-jar", "/path/to/your/downloaded/screenshot.jar", "-e", "screenshots/" + emulatorname + ".png"
    }
}

Opening all screenshots

To open all screenshots under MacOS to review them, just use the command

open screenshots/*

This will start Preview and open all images in a single window with a list of thumbnails so that you can quickly see if your app was started correctly on all these devices.

Selecting ListView elements

When displaying lists it can often happen that you have to click on an entry that is not visible on the screen. When it isn't visible on the screen then there is not even a view for it, so the normal test functions cannot find and click that view. That's what "onData" is for. Instead of searching for a visible view it searches the adapters of the visible views for matching elements. E.g.

onData(hasToString("Test")).perform(click());

clicks on the row of any ListView (or Spinner or other element with an adapter) that returns "Test" from its "toString()" method. If you have multiple objects with an adapter on the screen (e.g. a ListView and Spinners), you can limit the commmand to the ListView

onData(hasToString("Test")).inAdapterView(withClassName(Matchers.is(ListView.class.getName()))).perform(click());

UI Tests with XCode 7 and XCTestCase

In XCode 7 direct support for two types of tests has been added: Unit tests: These tests can access your project’s classes and are directly linked to your project. They are used for low-level tests where you test the functions of your classes. Usually one test class for one project class is created. UI tests: … Continue reading “UI Tests with XCode 7 and XCTestCase”

In XCode 7 direct support for two types of tests has been added:

  • Unit tests: These tests can access your project’s classes and are directly linked to your project. They are used for low-level tests where you test the functions of your classes. Usually one test class for one project class is created.
  • UI tests: These tests run independently of your project and perform UI actions. I.e. they cannot access the classes or data of your app. But they can use the app like a user, i.e. they can interact with it, tap buttons, enter text etc.

To add such tests to your existing project just choose “New > Target” in XCode 7:

Screen Shot 2015-11-01 at 04.05.36

It will create example classes that make it easier to start. To create a new UI test is also simple. Just create an empty function, e.g.

- (void)testEverything {
}

Then place the cursor in this function an click on the record button to start the simulator and record your actions as commands into the method:

Screen Shot 2015-11-01 at 04.20.56

To run the tests when you are finished just long click on the play button in XCode and select “Test”.

Snippets for UI test

Turning a switch on:

if ([tablesQuery.switches[@"My switch label"].value isEqualToString:@"0"]) {
    [tablesQuery.switches[@"My switch label"] tap];
}

Checking the position of a row in a table:

XCTAssertLessThanOrEqual([[app.staticTexts[@"My row label"] coordinateWithNormalizedOffset:CGVectorMake(0, 0)] screenPoint].y, 90);

Searching for a table row that starts with “Until:” (you can find a reference of the BEGINSWITH and similar commands here):

[[app.staticTexts elementMatchingPredicate:[NSPredicate predicateWithFormat:@"label BEGINSWITH 'Until:'"]] tap];

Displaying all currently visible elements:

NSLog(app.debugDescription);

Overview of the available functions:

http://masilotti.com/xctest-documentation/index.html