it-roy-ru.com

Как управлять разрешениями на время выполнения эспрессо-тестов на Android Marshmallow

Я использую эспрессо для тестирования, но иногда я пытаюсь получить образ из внешнего хранилища, и с помощью Marshmallow мне нужно разрешение Runtime, в противном случае произойдет сбой Exception и тест не пройден. 

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'

как я могу справиться с этим правильно? 

я должен написать тест для прав выполнения или есть способ отключить его для тестирования? 

я должен дать разрешения до запуска тестов, как она говорит здесь? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk

25
Caipivara

Вы можете создать задание Android для предоставления разрешения:

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

И это команда для запуска задачи: gradle grantDebugPermissions

10
Luiz Augusto

ОБНОВИТЬ! Теперь вы можете использовать Правило из библиотеки поддержки тестирования Android

Это более правильно использовать, чем пользовательские правила.

Устаревший ответ:

Вы можете добавить тестовое правило, чтобы повторно использовать код и добавить больше гибкости:

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

После этого вы можете использовать это правило в ваших тестовых классах:

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

Иметь ввиду:

  1. Правило не влияет на методы @Before, потому что после этого все правила выполняются
  2. executeShellCommand является асинхронным, и если вам нужны принятые разрешения сразу после запуска теста, подумайте о добавлении некоторой задержки
24
Denis Nek

Вы можете предоставлять и отзывать разрешения, используя:

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

Чтобы использовать тесты инструментария Java, вызовите этот метод из примеров Google: https://github.com/googlesamples/Android-testing/blob/ed62c450e43f859333b3113d44dd59f75971b529/ui/espresso/IntentsBasicSample/app/src/avaroidest ru/example/Android/тестирование/эспрессо/BasicSample/DialerActivityTest.Java # L94

23
Sebas LG

Вы можете использовать GrantPermissionRule . Это правило предоставит все запрошенные разрешения времени выполнения для всех методов тестирования в этом классе тестирования.

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

Вы можете легко добиться этого, предоставив разрешение перед началом теста. Например, если вы должны использовать камеру во время тестового прогона, вы можете предоставить разрешение следующим образом

@Before
public void grantPhonePermission() {

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

GrantPermissionRule находится в библиотеке поддержки тестирования Android , которую вы можете использовать в своих тестах для предоставления разрешения перед началом любых тестов.

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

В настройке с несколькими вариантами выполнения, какой бы ни была ваша инструментальная задача, скажем, connectedYourFlavorDebugAndroidTest, вы можете указать разрешения, которые вы хотите предоставить до запуска тестов на всех подключенных устройствах:

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

Смотрите фрагмент кода sfjava ниже , чтобы скопировать в build.gradle для генерации grantYourFlavorDebugPermissions задачи

4
riwnodennyk

Несколько небольших обновлений приведенного выше фрагмента (props to riwnodennyk), который отлично сработал для меня при сборке с SDK 24 и с инструментами версии 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
sfjava

Я реализовал решение, которое использует классы-обертки, переопределяя и конфигурируя варианты сборки. Решение довольно длинное для объяснения и находится здесь: https://github.com/ahasbini/AndroidTestMockPermissionUtils . Ему не нужно добавлять скрипт в систему сборки или выполнять перед запуском тестов.

Он еще не упакован в SDK, но основная идея состоит в том, чтобы переопределить функциональные возможности ContextWrapper.checkSelfPermission() и ActivityCompat.requestPermissions() для манипуляции и вернуть ложные результаты, вводя приложение в различные сценарии для тестирования, например: разрешение было отклонено, следовательно, приложение запросило его и завершило работу с предоставленным разрешением. Этот сценарий будет происходить, даже если приложение все время имело разрешение, но идея заключается в том, что его обманули поддельные результаты переопределенной реализации.

Кроме того, в реализации имеется класс TestRule с именем PermissionRule, который можно использовать в тестовых классах, чтобы легко имитировать все условия для беспроблемного тестирования разрешений. Также можно сделать утверждения, например, убедившись, что приложение вызвало requestPermissions().

2
ahasbini

Если вы используете новейшую библиотеку com.Android.support.test.espresso: espresso-core: 3.0.1 для эспрессо, это можно сделать в одной строке кода. Все, что вам нужно сделать, это просто добавить правило в класс Test и продолжать добавлять необходимые вам полномочия в качестве параметров функции для предоставления функции. Увидеть ниже:

@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