webentwicklung-frage-antwort-db.com.de

So verwalten Sie Laufzeitberechtigungen Android Marshmallow-Espresso-Tests

Ich verwende Espresso zu Testzwecken, aber manchmal versuche ich, ein externes Speichermedium abzuspeichern. Bei Marshmallow benötige ich eine Runtime-Berechtigung. Andernfalls kommt es zu einem Absturz der Ausnahme und der Test schlägt fehl. 

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'

wie kann ich das richtig machen? 

sollte ich test für Runtime-Berechtigungen schreiben, oder gibt es eine Möglichkeit, es für Tests zu deaktivieren? 

sollte ich Berechtigungen erteilen, bevor die Tests wie hier beschrieben ausgeführt werden? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk

25
Caipivara

Sie können eine Android-Gradel-Aufgabe erstellen, um die Berechtigung zu erteilen:

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()
            }
        }
    }
}

Und dies ist der Befehl zum Ausführen der Aufgabe: gradle grantDebugPermissions

10
Luiz Augusto

UPDATE! Jetzt können Sie Regel aus der Android Testing Support Library verwenden

Es ist sinnvoller als benutzerdefinierte Regeln zu verwenden.

Veraltete Antwort:

Sie können eine Testregel hinzufügen, um Code wiederzuverwenden und mehr Flexibilität hinzuzufügen:

/**
 * 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);
            }
        }
    }
}

Danach können Sie diese Regel in Ihren Testklassen verwenden:

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

Merken Sie sich:

  1. Die Regel wirkt sich nicht auf @Before-Methoden aus, da alle Regeln nach dieser Ausführung ausgeführt werden
  2. executeShellCommand ist asynchron. Wenn Sie gleich nach Testbeginn akzeptierte Berechtigungen benötigen, sollten Sie eine Verzögerung hinzufügen
24
Denis Nek

Sie können Berechtigungen erteilen und widerrufen, indem Sie:

adb Shell pm grant com.package.myapp Android.permission.<PERMISSION>
adb Shell pm revoke com.package.myapp Android.permission.<PERMISSION>

Um Java-Instrumentierungstests zu verwenden, rufen Sie diese Methode von Google-Beispielen auf: https://github.com/googlesamples/Android-testing/blob/ed62c450e43f811333b3113d44dd59f75971b529/ui/espresso/IntentsBasicSample/app/src/androidTest/Java/ de/beispiel/Android/testing/espresso/BasicSample/DialerActivityTest.Java # L94

23
Sebas LG

Sie können GrantPermissionRule verwenden. Diese Regel gewährt alle angeforderten Laufzeitberechtigungen für alle Testmethoden in dieser Testklasse.

@Rule 
public GrantPermissionRule mRuntimePermissionRule
            = GrantPermissionRule.grant(Manifest.permission.READ_PHONE_STATE);
10
vsvankhede

Sie können dies leicht erreichen, indem Sie vor dem Test eine Erlaubnis erteilen. Wenn Sie zum Beispiel die Kamera während des Testlaufs verwenden, können Sie die Berechtigung wie folgt erteilen

@Before
public void grantPhonePermission() {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        getInstrumentation().getUiAutomation().executeShellCommand(
                "pm grant " + getTargetContext().getPackageName()
                        + " Android.permission.CAMERA");
    }
}

In Android Testing Support Library gibt es die GrantPermissionRule, die Sie in Ihren Tests verwenden können, um vor dem Starten von Tests eine Berechtigung zu erteilen.

@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Android.Manifest.permission.CAMERA);
7
NSK

In der Multi-Flavor-Konfiguration können Sie unabhängig von Ihrer Instrumentierungsaufgabe, beispielsweise connectedYourFlavorDebugAndroidTest, die Berechtigungen angeben, die Sie vergeben möchten, bevor die Tests auf allen verbundenen Geräten ausgeführt werden:

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

Siehe sfjavas Snippet unten zum Kopieren in build.gradle, um eine grantYourFlavorDebugPermissions-Task zu generieren

4
riwnodennyk

Nur ein paar kleinere UPDATEs zu dem obigen Snippet (Requisiten für Riwnodennyk) - was für mich beim Bau gegen das SDK 24 und für die Tool-Version 24.0.0 großartig war:

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
sfjava

Ich habe eine Lösung implementiert, die Wrapper-Klassen nutzt und die Variantenkonfiguration überschreibt. Die Lösung ist ziemlich lang zu erklären und ist hier zu finden: https://github.com/ahasbini/AndroidTestMockPermissionUtils . Es muss kein Skript im Build-System hinzugefügt oder ausgeführt werden, bevor die Tests ausgeführt werden.

Es ist noch nicht in einem SDK verpackt, aber die Hauptidee ist, die zu manipulierenden Funktionalitäten von ContextWrapper.checkSelfPermission() und ActivityCompat.requestPermissions() zu überschreiben und gespielte Ergebnisse zurückzugeben, die die App in die verschiedenen zu testenden Szenarien überführen mit Erlaubnis. Dieses Szenario tritt auch dann auf, wenn die App die ganze Zeit über die Berechtigung hatte, die Idee ist jedoch, dass sie durch die simulierten Ergebnisse der überschreibenden Implementierung ausgetrickst wurde.

Darüber hinaus verfügt die Implementierung über eine TestRule-Klasse namens PermissionRule, die in den Testklassen verwendet werden kann, um alle Bedingungen zu simulieren, um die Berechtigungen nahtlos zu testen. Es können auch Zusicherungen gemacht werden, die beispielsweise sicherstellen, dass die App requestPermissions() aufgerufen hat.

2
ahasbini

Wenn Sie die neueste Bibliothek "com.Android.support.test.espresso: espresso-core: 3.0.1" für Espresso verwenden, kann dies in einer einzigen Codezeile erfolgen. Sie müssen lediglich eine Regel in der Test-Klasse hinzufügen und die erforderlichen Berechtigungen als Funktionsparameter hinzufügen, um die Funktion zu gewähren. Siehe unten:

@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
Abhishek Dhotre
    Android.support.test.uiautomator.UiDevice mDevice;

 @Before
    public void setUp() throws Exception {
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    }

@Test
    public void testMainActivityScreenshots() {
               allowPermissionsIfNeeded();//allowPermissions on Activity
}

   private void allowPermissionsIfNeeded()  {
        if (Build.VERSION.SDK_INT >= 23) {
            UiObject allowPermissions = mDevice.findObject(
                    new UiSelector().className("Android.widget.Button")
                    .resourceId("com.Android.packageinstaller:id/permission_allow_button"));// get allow_button Button by id , because on another device languages it is not "Allow"
            if (allowPermissions.exists()) {
                try {
                    allowPermissions.click();
                    allowPermissionsIfNeeded();//allow second Permission
                } catch (UiObjectNotFoundException e) {
                    Timber.e(e, "There is no permissions dialog to interact with ");
                }
            }
        }
    }
0
NickUnuchek