Logo preload
close Logo

Automated UI Testing and Fulcrum Android Permissions

January 1, 2016

Recently, we’ve started taking the time to write some automated tests for the user interface of our Android application. The goal, as with most tests, is to prevent regressions when we push out new features and make changes to the app. There’s quite a few frameworks you can use, and we’ve made good progress using Espresso. There have been some hurdles though; one of the biggest bumps was figuring out a way our tests could work with the new permissions model on Android Marshmallow.

In a previous post, I talked about how in Android Marshmallow permissions are now requested and granted when they are needed, instead of when the app is installed. This new method of requesting and granting permissions is performed through a popup that appears when the user tries to access a feature that requires a permission (the camera, the device’s location, etc.).

The reason this matters to our automated user interface tests is we have to account for those permissions popups. As you can imagine, there’s quite a few places a popup might appear. Compounding the problem is the fact that a popup only appears if the app doesn’t have the necessary permission, will continue to appear until the permission is granted (or the user says “don’t ask me again”), and then won’t appear again!

With enough time, testing, and conditional code, we could probably handle all of the cases mentioned in the previous paragraph. However, there’s another problem that pretty much makes it impossible to work with the popups. Since the Android OS is what manages that popup, grabbing the ‘Allow’ button and pressing it using Espresso (our chosen framework) isn’t possible. You can do this using another framework called UI Automator, but the problem with UI Automator is it has a minimum SDK requirement of 18. This means we wouldn’t be able to run our tests on all the versions of Android we support; this kind of defeats the purpose of writing automated ui tests to begin with!

So, since it isn’t feasible for us to grant permissions while the tests are running on the app, our solution has to involve granting permissions before the tests run. While this might sound easy, it’s actually pretty tricky.

When looking for resources on how to grant permissions for unit tests, I found a couple fairly detailed walkthroughs, but not much else. I tried many combinations of the information I found, but this is the only resource that got me close enough to a working solution that I could figure the rest out on my own. In short, you have to create a gradle task that runs after the app is installed. This task runs a shell script which executes adb commands to grant the permissions you need. I did have to make some modifications, specifically:

  • I think the $ANDROID_HOME environment variable wasn’t created on my machine; I’ve run into a similar issue before. I tried creating it, but gradle still couldn’t find it or something. Anyway, so inside build.gradle I got the path to adb and sent it to the shell script. This also keeps the tests working on any coworkers’ machines (as opposed to hardcoding the path)
  • The name of the buildType used for our tests is debug so all Mocks became debugs.
  • Since our shell script sits in the same location as build.gradle, it wasn’t necessary to manipulate the path to the script.

Here is the relevant portions of our code:

build.gradle

// Grant necessary permissions to avoid test failure because of ui sync.
task grantPermissions(type: Exec, dependsOn: ‘installDebug’) {
   def adb = android.getAdbExe().toString()

   commandLine “sh grant_permissions.sh $adb com.spatialnetworks.fulcrum.debug”.split(” “)
}

afterEvaluate {
   // When launching tests from Android Studio, it seems that only the assemble tasks
   // get called directly, not the install* versions
   tasks.each { task ->
       if (task.name.startsWith(‘assembleDebugAndroidTest’)) {
           task.dependsOn grantPermissions
       }
   }
}

grant_permissions.sh

// grant_permissions.sh
#!/bin/bash
#
# argument: apk adb package
# ex: sh grant_permissions.sh <path_to_adb> <package>

adb=$1
package=$2

if [ “$#” = 0 ]; then
   echo “No parameters found, run with sh grant_permissions.sh <path_to_adb> <package>”
   exit 0
fi

# get all the devices
devices=$($adb devices | grep -v ‘List of devices’ | cut -f1 | grep ‘.’)

for device in $devices; do
   echo “Setting permissions to device” $device “for package” $package
   $adb -s $device shell pm grant $package android.permission.ACCESS_COARSE_LOCATION
   $adb -s $device shell pm grant $package android.permission.ACCESS_FINE_LOCATION
   $adb -s $device shell pm grant $package android.permission.RECORD_AUDIO
   $adb -s $device shell pm grant $package android.permission.CAMERA
done

Here are some notes on some of the other resources I found, and why they didn’t work for us:

  • We tried writing a gradle task and adding it to our tests’ run configuration through Android Studio. This wouldn’t work on the first run after the app was installed: BadArgumentException: Unknown Package…. I believe this is because there’s some timing issue or something between when the app is installed on the device and made available, and when the task is executed (even though we specified the task’s dependencies..?).
  • We also tried this (granting all permissions without using a shell script), but it seemed to only actually grant the last permission. For example, if we tried to grant location, record audio, and camera, camera would be the only permission actually granted. I also remember it being a pain trying to get the task to run at the right spot with this approach as well.

It’s quite tricky to write unit tests that handle the Marshmallow permissions popups, and even harder to find resources on how to handle those popups. By detailing what we did and how it’s working for us, we’re hoping it’s easier for others to get it working. Feel free to hit us up on support, email, or whatever!