Comment gérer les permissions d'exécution android marshmallow espresso tests

j'utilise espresso pour tester mais parfois j'essaie d'obtenir une image de stockage externe et avec marshmallow j'ai besoin d'une permission Runtime sinon il y aura un crash D'Exception et le test échouera.

androidTestCompile 'com.android.support.test:runner:0.4'
androidTestCompile 'com.android.support.test:rules:0.4'
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.1') {
    // this library uses the newest app compat v22 but the espresso contrib still v21.
    // you have to specifically exclude the older versions of the contrib library or
    // there will be some conflicts
    exclude group: 'com.android.support', module: 'appcompat'
    exclude group: 'com.android.support', module: 'support-v4'
    exclude module: 'recyclerview-v7'
}
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.squareup.retrofit:retrofit-mock:1.9.0'
androidTestCompile 'com.squareup.assertj:assertj-android:1.1.0'
androidTestCompile 'com.squareup.spoon:spoon-client:1.2.0'

comment puis-je gérer ce droit?

devrais-je écrire un test pour les permissions D'exécution ou il y a un moyen de le désactiver pour le tester?

dois-je donner des permissions avant que les tests ne fonctionnent comme elle le dit ici? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk

24
demandé sur Vadim Kotov 2015-09-25 20:02:35

11 réponses

vous pouvez créer une tâche gradle Android pour accorder la 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()
            }
        }
    }
}

Et c'est la commande à exécuter la tâche: gradle grantDebugPermissions

10
répondu Luiz Augusto 2017-01-23 20:12:28

Vous pouvez accorder et révoquer les permissions en utilisant:

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

à utiliser à partir de Java instrumentation tests appeler cette méthode à partir de 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

23
répondu Sebas LG 2016-08-05 14:20:18

mise à jour! Maintenant, vous pouvez utiliser Règle à partir d'Android Tests de Soutien de la Bibliothèque

il est plus approprié d'utiliser que les règles personnalisées.

réponse périmée:

vous pouvez ajouter une règle de test pour réutiliser le code et ajouter plus de flexibilité:

/**
 * This rule adds selected permissions to test app
 */

public class PermissionsRule implements TestRule {

    private final String[] permissions;

    public PermissionsRule(String[] permissions) {
        this.permissions = permissions;
    }

    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {

                allowPermissions();

                base.evaluate();

                revokePermissions();
            }
        };
    }

    private void allowPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for (String permission : permissions) {
                InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
                        "pm grant " + InstrumentationRegistry.getTargetContext().getPackageName()
                                + " " + permission);
            }
        }
    }

    private void revokePermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for (String permission : permissions) {
                InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
                        "pm revoke " + InstrumentationRegistry.getTargetContext().getPackageName()
                                + " " + permission);
            }
        }
    }
}

Après cela, vous pouvez utiliser cette règle dans vos classes de test:

@Rule
public final PermissionsRule permissionsRule = new PermissionsRule(
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS});

Conserver à l'esprit:

  1. Règle n'a pas d'incidence sur @Avant méthodes parce que toutes les règles sont exécuté après que
  2. executeShellCommand est asynchrone et si vous avez besoin de permissions acceptées juste après le début du test, pensez à ajouter un délai
23
répondu Denis Nek 2017-07-26 02:21:13

Vous pouvez y parvenir facilement en accordant la permission avant de commencer le test. Par exemple si vous êtes censé utiliser l'appareil pendant le test, vous pouvez accorder l'autorisation comme suit

@Before
public void grantPhonePermission() {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        getInstrumentation().getUiAutomation().executeShellCommand(
                "pm grant " + getTargetContext().getPackageName()
                        + " android.permission.CAMERA");
    }
}
9
répondu Sachini Samarasinghe 2017-03-03 04:20:08

Vous pouvez utiliser Grantpermissionrulle. Cette règle va accorder toutes les permissions d'exécution demandées pour toutes les méthodes de test dans cette classe de test.

@Rule 
public GrantPermissionRule mRuntimePermissionRule
            = GrantPermissionRule.grant(Manifest.permission.READ_PHONE_STATE);
9
répondu vsvankhede 2017-11-18 18:02:59

dans la configuration multi-saveurs, quelle que soit votre tâche d'instrumentation, disons connectedYourFlavorDebugAndroidTest, vous pouvez spécifier les permissions que vous voulez avoir accordées avant que les tests soient lancés sur tous les périphériques connectés:

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

Voir sfjava de l'extrait de code ci-dessous copier dans build.gradle pour générer grantYourFlavorDebugPermissions tâche

4
répondu riwnodennyk 2017-05-23 12:10:28

Il n'est GrantPermissionRule Android Tests De Soutien De La Bibliothèque, que vous pouvez utiliser dans vos tests d'accorder une autorisation avant de commencer les tests.

@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA);
4
répondu NSK 2018-03-14 10:40:10

Juste quelques Mises à jour mineures dans l'extrait ci-dessus ( accessoires riwnodennyk ) -- qui a très bien fonctionné pour moi lors de la construction contre SDK 24 et avec les outils de la 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);
    }
}
3
répondu sfjava 2016-06-30 22:50:24

Si vous utilisez la dernière version com.Android.soutien.test.expresso:expresso-core:3.0.1 " bibliothèque pour l'espresso cela peut être fait en une seule ligne de code. Tout ce que vous devez faire est d'ajouter une règle dans la classe Test et de continuer à ajouter les permissions dont vous avez besoin comme paramètres de fonction pour accorder la fonction. Voir ci-dessous:

@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

2
répondu Abhishek Dhotre 2017-10-30 16:52:38

j'ai mis en place une solution qui utilise les classes d'enrubannage, la modification de la configuration et la construction de variantes. La solution est assez longue à expliquer et se trouve ici:https://github.com/ahasbini/AndroidTestMockPermissionUtils