Android Espresso UI testing

Runtime permissions and Espresso done right

UI testing on Android improved a lot last year with Espresso, but it still can cause headaches from time to time. A good example would be the runtime permissions (introduced in Marshmallow), asking the user to grant specific (dangerous) permissions via system dialogs, hence making Espresso loose control over the app under test.

Note that this is just a small part of the new support library, but it made me very happy.

This is because Espresso can only target the Views inside the app, it can't interact with other apps or the OS, which also includes system dialogs. So when presenting a permission dialog, the test is stuck - it cannot interact with the underlying views, failing all asserts. Luckily, there was a solution: one could use the UIAutomator library, designed for UI testing across apps and the system.

You probably have a function like this, just to call it in every @Test-annotated method where applicable:

fun grantPermissionsIfNecessary() {
  if (Build.VERSION.SDK_INT >= 23) {
    val allowPermissions = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).findObject(UiSelector().text("ALLOW"))
    if (allowPermissions.exists()) {
      try {
      } catch (e: UiObjectNotFoundException) {
        Timber.e(e, "No permission dialog found.")

What this snippet does is that it looks for an object on the screen with the text ALLOW, and tries to click on it - pretty standard stuff, similar to the way you'd do it with Espresso.

Why does this suck:

  • complicated to support multiple languages (allow is in English, and there's no String resource ID - what if the test cases are run on a different locale?);
  • using an extra library, just for one feature (at least in our case) - to get the permission dialog out of the way;
  • lot of redundant code - you'll have to call this in every @Test method that involves activities requiring permissions;
  • kind of hacky.

Enter GrantPermissionRule

A couple of days ago Google outed version 1.0.0 of the Android Testing Support library in a blog post, containing a simple solution to the problem detailed above, called GrantPermissionRule. Out with the old, in with the new - switching to the new rule looks like this:

@Rule @JvmField
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION)

This single line of code grants every permission listed as parameter in the grant method with immediate effect. In other words, the app will be treated like if the permissions were already granted - no more dialogs. Also, no need to stick it into each and every method (just into the classes). Cleaner, simpler, nicer.

To including the library, just update the dependencies' version to the following:

dependencies {
  testImplementation "junit:junit:4.12"
  androidTestImplementation ""
  androidTestImplementation ""

Note that this is just a small part of the new support library, but it made me very happy - I was utterly annoyed by the way permission management worked before. See the complete release notes here.