J'utilise l'espresso pour les tests, mais parfois, j'essaie d'obtenir une mémoire externe sous forme d'image. Avec Marshmallow, j'ai besoin d'une autorisation d'exécution. Dans le cas contraire, un crash se produira 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?
dois-je écrire un test pour les autorisations d'exécution ou un moyen de le désactiver pour les tests?
devrais-je donner des autorisations avant que les tests ne se déroulent comme elle le dit ici? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk
Vous pouvez créer une tâche de dégradé Android pour accorder une autorisation:
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 voici la commande pour exécuter la tâche:
gradle grantDebugPermissions
METTRE À JOUR! Vous pouvez maintenant utiliser Rule depuis Android Testing Support Library
Il est plus approprié d'utiliser que des règles personnalisées.
Réponse obsolète:
Vous pouvez ajouter une règle de test pour réutiliser du 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});
Garder en tete:
Vous pouvez utiliser GrantPermissionRule . Cette règle accordera toutes les autorisations d'exécution demandées pour toutes les méthodes de test de cette classe de test.
@Rule
public GrantPermissionRule mRuntimePermissionRule
= GrantPermissionRule.grant(Manifest.permission.READ_PHONE_STATE);
Vous pouvez y parvenir facilement en accordant une autorisation avant de commencer le test. Par exemple, si vous êtes censé utiliser l'appareil photo pendant l'exécution du 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");
}
}
Il y a GrantPermissionRule dans Android Testing Support Library , que vous pouvez utiliser dans vos tests pour accorder une autorisation avant de commencer des tests.
@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Android.Manifest.permission.CAMERA);
Dans la configuration multi-saveurs, quelle que soit votre tâche d'instrumentation, disons connectedYourFlavorDebugAndroidTest
, vous pouvez spécifier les autorisations que vous souhaitez attribuer avant l'exécution des tests sur tous les périphériques connectés:
gradlew grantYourFlavorDebugPermissions -Ppermissions=Android.permission.ACCESS_FINE_LOCATION,Android.permission.ACCESS_COARSE_LOCATION
Voir l'extrait de sfjava ci-dessous à copier dans build.gradle
pour générer la tâche grantYourFlavorDebugPermissions
Quelques mises à jour mineures à l'extrait ci-dessus (accessoires de riwnodennyk) - ce qui a très bien fonctionné pour moi lorsque je compilais contre le SDK 24 et avec 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);
}
}
J'ai implémenté une solution qui exploite les classes wrapper, redéfinit et construit les variantes de configuration. La solution est assez longue à expliquer et se trouve ici: https://github.com/ahasbini/AndroidTestMockPermissionUtils . Il n'a pas besoin de script pour être ajouté dans le système de construction ou exécuté avant d'exécuter les tests.
Il n’a pas encore été emballé dans un sdk mais l’idée principale est de remplacer les fonctionnalités de ContextWrapper.checkSelfPermission()
et ActivityCompat.requestPermissions()
à manipuler et à renvoyer les résultats fictifs capturant l’application dans les différents scénarios à tester, comme par exemple: l’autorisation a été refusée. avec la permission accordée. Ce scénario se produira même si l'application avait l'autorisation depuis le début, mais l'idée est qu'il a été trompé par les résultats simulés de l'implémentation prépondérante.
De plus, l'implémentation a une classe TestRule
appelée PermissionRule
qui peut être utilisée dans les classes de test pour simuler facilement toutes les conditions permettant de tester les autorisations de manière transparente. Des affirmations peuvent également être faites, comme s’assurer que l’application a appelé requestPermissions()
par exemple.
Si vous utilisez la dernière librairie pour espresso 'com.Android.support.test.espresso: espresso-core: 3.0.1', vous pouvez utiliser une seule ligne de code. Tout ce que vous avez à faire est simplement d'ajouter une règle dans la classe Test et de continuer à ajouter les autorisations dont vous avez besoin en tant que paramètres de fonction pour accorder une 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
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 ");
}
}
}
}