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
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
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
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:
- Règle n'a pas d'incidence sur @Avant méthodes parce que toutes les règles sont exécuté après que
- 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
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");
}
}
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);
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
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);
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);
}
}
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
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