Android Marshmallow: Test Permissions with Espresso

Android Marshmallow: Test permissions with Espresso?

With the new release of the Android Testing Support Library 1.0, there's a GrantPermissionRule that you can use in your tests to grant a permission before starting any tests.

@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);

Kotlin solution

@get:Rule var permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION)

@get:Rule must be used in order to avoid java.lang.Exception: The @Rule 'permissionRule' must be public. More info here.

How to manage Runtime permissions android marshmallow espresso tests

You can create an Android gradle task to grant permission:

android.applicationVariants.all { variant ->
def applicationId = variant.applicationId
def adb = android.getAdbExe().toString()
def variantName = variant.name.capitalize()
def grantPermissionTask = tasks.create("grant${variantName}Permissions") << {
"${adb} devices".execute().text.eachLine {
if (it.endsWith("device")){
def device = it.split()[0]
println "Granting permissions on devices ${device}"
"${adb} -s ${device} shell pm grant ${applicationId} android.permission.CAMERA".execute()
"${adb} -s ${device} shell pm grant ${applicationId} android.permission.ACCESS_FINE_LOCATION".execute()
}
}
}
}

And this is the command to run the task:

gradle grantDebugPermissions

Unable to test code requiring CHANGE_NETWORK_STATE / WRITE_SETTINGS permission on Marshmallow

Because WRITE_SETTINGS is a sensitive permission, you won't be able to grant it using GrantPermissionRule in API 23. You will likely end up needing to use UIAutomator in your tests to select the appropriate response in the permissions management screen.

Android Marshmallow: How to allow Runtime Permissions programatically?

For Marshmallow or later permissions are not granted at install time and must be requested when required at runtime (if not granted previously.)

To do this, you need to run ActivityCompat.requestPermissions() to pop up the systems permissions dialog in your Activity, at the time when the user is undertaking an action that requires additional system permissions.

An example of this for the WRITE_EXTERNAL_STORAGE permission would be:

ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
WRITE_EXTERNAL_STORAGE_REQUEST_CODE
);

Note: WRITE_EXTERNAL_STORAGE_REQUEST_CODE is an arbitrary integer constant you should define elsewhere.

The permissions you request should also be declared in your AndroidManifest.xml. In this example the declaration would be:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

In order to handle the system permissions dialog response you will also need to implement onRequestPermissionsResult() in your Activity. For this example the code would be similar to

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
return; //permission not granted, could also optionally log an error
}
if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
//Do whatever you needed the write permissions for
}
}

If you are automating your app through Espresso, UIAutomator and/or some other UI testing framework you will need to anticipate and click the system dialog during your test, which can be accomplished with the following test code:

private void allowPermissionsIfNeeded()  {
if (Build.VERSION.SDK_INT >= 23) {
UiObject allowPermissions = mDevice.findObject(new UiSelector().text("Allow"));
if (allowPermissions.exists()) {
try {
allowPermissions.click();
} catch (UiObjectNotFoundException e) {
Timber.e(e, "There is no permissions dialog to interact with ");
}
}
}
}

A more comprehensive explanation of testing System UI Permissions is available here.



Related Topics



Leave a reply



Submit