2015-09-25 9 views
21

Ich benutze espresso zum Testen, aber manchmal versuche ich, ein Bild von externen Speicher zu bekommen und mit Marshmallow brauche ich eine Runtime-Erlaubnis, sonst wird es eine Ausnahme Absturz und der Test wird fehlschlagen .So verwalten Runtime-Berechtigungen android Marshmallow Espresso-Tests

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 verwalten?

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

sollte ich Berechtigungen geben, bevor die Tests laufen, wie sie hier sagt? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk

+1

Mögliches Duplikat [Android Eibisch: Test-Berechtigungen mit Espresso] (http://stackoverflow.com/questions/33929937/android-marshmallow-test-permissions-with-espresso) –

+0

@RocBoronat in diesem Fall, dass Frage sollte sei der duplizierte, dieser ist älter – Caipivara

+0

völlig zustimmen, aber es scheint, dass der andere mehr Engagement von der Gemeinschaft hat.Die Antworten sind wirklich konstruktiv. Darüber hinaus löst die akzeptierte Antwort dieser Frage nicht das Problem ... also, für mich sind nicht die Begriffe "wer ist zuerst angekommen", sondern "welche Fragen und Antworten sind für die Gemeinschaft nützlicher". –

Antwort

9

Sie können eine Android gradle Aufgabe erstellen Genehmigung 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, um die Aufgabe auszuführen: gradle grantDebugPermissions

+0

für mich gibt es etwas, das aussieht wie ein Berechtigungsproblem ... ist die Aufgabe erlaubt, solche Aktionen standardmäßig durchzuführen, oder möglicherweise das Betriebssystem verbietet dies? wie: * Was schief gelaufen ist: Ausführung fehlgeschlagen für Task ': Remote: grantAllDebugPermissions'. > Ein Problem beim Starten des Prozesses 'Befehl' [email protected] '' irgendwelche Ideen? – cV2

+0

Diese Lösung (https://stackoverflow.com/a/42570592/1430390) ist eleganter. – Toochka

+0

Jetzt ist es besser zu verwenden https://developer.android.com/reference/android/support/test/rule/GrantPermissionRule.html –

22

können Sie erteilen und entziehen Berechtigungen mit:

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

Zur Nutzung von Java Instrumentierung Tests diese Methode von Google Proben nennen: 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

aber ich sollte nicht testen, wie der Code auf diesen neuen Code "Runtime-Berechtigungen verwalten" reagiert? – Caipivara

+1

Sie sollten die Testfälle trennen, alle Speicherfunktionen sollten mit gewährten Berechtigungen getestet werden und die deaktivierten + erteilten/abgelehnten Berechtigungen sollten in isolierten Szenarien getestet werden. Der einfachste Weg, diese Berechtigungsszenarios zu testen, ist Unit-Tests und die Aufrufe von Berechtigungsmethoden zu verspotten (die Systemaufrufe können in Ihre eigenen Methoden eingeschlossen werden) und Sie können den gesamten Fluss mit Robolectric testen, nicht einmal Espresso, es sei denn Sie interessieren sich wirklich für die Pixel, die in diesem Fluss gezeichnet werden. –

+0

also .. warum ist onView (withText ("ALLOW")). Ausführen (click()); funktioniert nicht, wenn Espresso gebeten wird, die Erlaubnis zu erteilen? – Jeroen

3

Im Multi-Aroma-Setup, was auch immer Ihre Instrumentierung Aufgabe Geben Sie beispielsweise connectedYourFlavorDebugAndroidTest an. Sie können die Berechtigungen angeben, die gewährt werden sollen, bevor die Tests für alle verbundenen Geräte ausgeführt werden:

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

sfjava's snippet below Siehe in build.gradle zu kopieren grantYourFlavorDebugPermissions Aufgabe

+0

Die obige Lösung funktioniert großartig ... thx! Ich habe nur ein paar Updates (unten) zu dem von Ihnen geposteten Snippet; da einige Dinge fehlten oder sich einfach leicht geändert haben, z. in der IDevice-Schnittstelle, um beispielsweise das "apiLevel" des Geräts zu erhalten. BTW haben noch nicht Square "Spoon" verwendet (dh um UI-Tests auf einer Reihe von Geräten gleichzeitig auszuführen), aber es sieht so aus, als ob es auch einige gute Hinweise/Snippets in diesem Projekt gibt gepostet - zB in ihren SpoonUtils (und SpoonRunner) Klassen - unter anderem bin ich mir sicher. Thx für alle Hinweise! – sfjava

+0

Ja, danke für das Update – riwnodennyk

2

Nur ein paar kleineren Änderungen zu dem oben Snippet zu generieren (Requisiten riwnodennyk) - was gut für mich gearbeitet, wenn sie gegen SDK Aufbau 24 und mit Werkzeugen 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); 
    } 
} 
20

UPDATE! Jetzt können Sie verwenden Rule from Android Testing Support Library

Es ist richtiger zu verwenden als benutzerdefinierte Regeln.

Veraltete Antwort:

Sie Testregel Code wiederverwenden hinzufügen und mehr Flexibilität hinzu:

/** 
* 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 Ihrer Testklasse verwenden können:

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

Denken Sie daran:

  1. Regel wirkt sich nicht auf @Before Methoden, da alle Regeln nach diesem
  2. executeShellCommand ausgeführt sind asynchron und wenn Sie richtig akzeptiert Berechtigungen müssen nach dem Test eine gewisse Verzögerung
+3

Ich mag dies Lösung und es funktioniert gut für mich, nachdem behoben, dass die 'executeShellCommand' in' allowPermissions 'erfordert Synchronisation (Lesen der zurückgegebenen Datei-Deskriptor/Stream bis zum Ende). Andernfalls wird der Test möglicherweise gestartet, bevor die Berechtigungen tatsächlich angewendet wurden. Und im Allgemeinen sollten die von 'executeShellCommand' zurückgegebenen Dateideskriptoren geschlossen sein. – masc3d

+2

plus die 'revokePermissions()' sollte in einem 'finally'-Block sein, um sicher zu sein. – masc3d

+0

@ masc3d können Sie Ihren Synchronisierungscode angeben? –

0
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 "); 
       } 
      } 
     } 
    } 
7

Zugabe begann betrachten Sie können dies leicht erreichen, indem Sie vor dem Start des Tests die Berechtigung erteilen. Zum Beispiel, wenn Sie die Kamera während des Testlaufs verwenden sollen, können Sie die Erlaubnis erteilen, wie

@Before 
public void grantPhonePermission() { 

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

folgen können Sie 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); 
+1

Danke, anderer Weg ist https://github.com/stocky/spoon-gradle-plugin, um die Tests zu starten und 'löffel {grantAllPermissions = true}', um alle standardmäßig zu aktivieren – Caipivara

0

Wenn Sie den neuesten verwenden ‚com.android.support.test.espresso: Espresso-Kern: 3.0.1‘ Bibliothek für Espresso in einer einzigen Codezeile erfolgt diese kann. Sie müssen lediglich eine Regel in der Test-Klasse hinzufügen und die Berechtigungen hinzufügen, die Sie als Funktionsparameter benötigen, um die Funktion zu erteilen. 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

1

Ich habe eine Lösung implementiert, die Wrapper-Klassen nutzt, überschreiben und Variantenkonfiguration zu bauen. 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 Grundidee ist es, die Funktionalitäten von ContextWrapper.checkSelfPermission() und ActivityCompat.requestPermissions() außer Kraft zu setzen manipuliert werden und verspottet Ergebnisse angezeigt, die App in die verschiedene Szenarien tricksen wie getestet werden: die Erlaubnis wurde daher die App verweigert forderte es und endete mit erteilter Erlaubnis. Dieses Szenario wird auch dann auftreten, wenn die App die ganze Zeit über die Berechtigung hatte, die Idee jedoch darin besteht, dass sie durch die verspotteten Ergebnisse der übergeordneten Implementierung ausgetrickst wurde.

Darüber hinaus verfügt die Implementierung über eine TestRule genannte PermissionRule Klasse, die in den Testklassen verwendet werden kann, um alle Bedingungen zum nahtlosen Testen der Berechtigungen zu simulieren. Auch Aussagen können gemacht werden, wie zum Beispiel sicherstellen, dass die App requestPermissions() aufgerufen hat.

Verwandte Themen