How to manage Runtime permissions android marshmallow espresso tests

后端 未结 12 865
孤独总比滥情好
孤独总比滥情好 2020-12-05 02:33

I\'m using espresso for testing but sometimes I try to get an image form external storage and with marshmallow I need a Runtime permission otherwise there will be an Excepti

相关标签:
12条回答
  • 2020-12-05 03:07

    Just a few minor UPDATEs to the above snippet ( props to riwnodennyk ) -- which worked great for me when building against SDK 24 and with tools version 24.0.0:

    import com.android.ddmlib.AndroidDebugBridge
    import com.android.ddmlib.IShellOutputReceiver
    import com.android.ddmlib.IDevice
    
    import java.util.concurrent.TimeUnit
    
    android.applicationVariants.all { variant ->
        def applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join()
        def grantPermissionsTask = tasks.create("grant${variant.name.capitalize()}Permissions") << {
            if (!project.hasProperty('permissions')) {
                throw new GradleException("Please add the comma-separated command line parameter, for example -Ppermissions=android.permission.WRITE_EXTERNAL_STORAGE")
            }
            AndroidDebugBridge adb = initAdb(android.getAdbExe().toString())
            grantPermissionsOnAllConnectedDevice(adb, applicationId, project.properties['permissions'].split(','))
        }
        grantPermissionsTask.description = "Grants permissions for ${variant.name.capitalize()}."
        grantPermissionsTask.dependsOn "install${variant.name.capitalize()}"
    }
    
    public static Object grantPermissionsOnAllConnectedDevice(AndroidDebugBridge adb, String applicationId, String[] permissionNames) {
        return adb.getDevices().each {
            device ->
                int apiLevel = Integer.parseInt(device.getProperty(IDevice.PROP_BUILD_API_LEVEL))
                if (0 <  apiLevel && apiLevel < 23) {
                    println "\nSkipping granting permissions for " + device.serialNumber + " because has API level " + device.apiLevel + " < 23"
                    return
                }
    
                println "\nGranting permissions for " + applicationId + " on " + device.serialNumber
    
                permissionNames.each {
                    permissionName ->
                        def shellGrantCommand = "pm grant " + applicationId + " " + permissionName
                        println(shellGrantCommand)
                        device.executeShellCommand(shellGrantCommand, new IShellOutputReceiver() {
                            @Override
                            void addOutput(byte[] data, int offset, int length) {
                                println new String(data[offset..(offset + length - 1)] as byte[])
                            }
    
                            @Override
                            void flush() {
    
                            }
    
                            @Override
                            boolean isCancelled() {
                                return false
                            }
                        })
                }
        }
    }
    
    public static AndroidDebugBridge initAdb(String path) {
        AndroidDebugBridge.initIfNeeded(false)
        AndroidDebugBridge adb = AndroidDebugBridge.createBridge(path, false)
        waitForAdb(adb, 15000)
        return adb
    }
    
    private static void waitForAdb(AndroidDebugBridge adb, long timeOutMs) {
        long sleepTimeMs = TimeUnit.SECONDS.toMillis(1);
        while (!adb.hasInitialDeviceList() && timeOutMs > 0) {
            try {
                Thread.sleep(sleepTimeMs);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            timeOutMs -= sleepTimeMs;
        }
        if (timeOutMs <= 0 && !adb.hasInitialDeviceList()) {
            throw new RuntimeException("Timeout getting device list.", null);
        }
    }
    
    0 讨论(0)
  • 2020-12-05 03:10

    If you are using the latest 'com.android.support.test.espresso:espresso-core:3.0.1' library for espresso this can be done in a single line of code. All you need to do is just add a rule in in Test class and keep adding the permissions you need as function parameters to grant function. See below:

    @Rule
    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule .grant(Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.BLUETOOTH,Manifest.permission.RECORD_AUDIO);
    

    https://developer.android.com/reference/android/support/test/rule/GrantPermissionRule.html

    0 讨论(0)
  • 2020-12-05 03:22

    You can grant and revoke permissions using:

    adb shell pm grant com.package.myapp android.permission.<PERMISSION>
    adb shell pm revoke com.package.myapp android.permission.<PERMISSION>
    

    To use from Java instrumentation tests call this method from Google samples: https://github.com/googlesamples/android-testing/blob/ed62c450e43f859333b3113d44dd59f75971b529/ui/espresso/IntentsBasicSample/app/src/androidTest/java/com/example/android/testing/espresso/BasicSample/DialerActivityTest.java#L94

    0 讨论(0)
  • 2020-12-05 03:22

    In the multi-flavor setup, whatever your instrumentation task is, let's say connectedYourFlavorDebugAndroidTest, you may specify the permissions you want to have granted before the tests are run on all the connected devices:

    gradlew grantYourFlavorDebugPermissions -Ppermissions=android.permission.ACCESS_FINE_LOCATION,android.permission.ACCESS_COARSE_LOCATION

    See sfjava's snippet below to copy into build.gradle to generate grantYourFlavorDebugPermissions task

    0 讨论(0)
  • 2020-12-05 03:23

    I've implemented a solution which leverages wrapper classes, overriding and build variant configuration. The solution is quite long to explain and is found over here: https://github.com/ahasbini/AndroidTestMockPermissionUtils. It doesn't need any script to be added in the build system or executed before running the tests.

    It is not yet packed in an sdk but the main idea is to override the functionalities of ContextWrapper.checkSelfPermission() and ActivityCompat.requestPermissions() to be manipulated and return mocked results tricking the app into the different scenarios to be tested like: permission was denied hence the app requested it and ended with granted permission. This scenario will occur even if the app had the permission all along but the idea is that it was tricked by the mocked results from the overriding implementation.

    Furthermore the implementation has a TestRule called PermissionRule class which can be used in the test classes to easily simulate all of the conditions to test the permissions seamlessly. Also assertions can be made like ensuring the app has called requestPermissions() for example.

    0 讨论(0)
  • 2020-12-05 03:25

    adb install -g package.apk

    I had a similar issue in automated testing: when launching the app the permission dialog popped up. The adb shell pm grant … command did not help but there is the -g option of adb install to grant all permissions upon install. This solved the problem for me.

    From adb --help:

    Android Debug Bridge version 1.0.41
    Version 30.0.5-6877874
    […]
    app installation (see also `adb shell cmd package help`):
     install [-lrtsdg] [--instant] PACKAGE
         push a single package to the device and install it
    […]
        -g: grant all runtime permissions
    […]
    
    0 讨论(0)
提交回复
热议问题