42

Android Marshmallow引入的新权限方案需要在运行时检查特定权限,这意味着需要根据用户是拒绝还是允许访问来提供不同的流程。Android Marshmallow:用Espresso测试权限?

由于我们使用Espresso在我们的应用上运行自动UI测试,我们如何模拟或更新权限状态以测试不同的场景?

+0

可能重复[如何管理运行时权限android marshmallow espresso测试](http://stackoverflow.com/questions/32787234/how-to-manage-runtime-permissions-android-marshmallow-espresso-tests) – Caipivara

+0

尝试这可能会对你有所帮助:-http://stackoverflow.com/a/41221852/5488468 –

回答

28

给人以这样的静态方法一试,当您的手机是英语语言环境:

private static void allowPermissionsIfNeeded() { 
    if (Build.VERSION.SDK_INT >= 23) { 
     UiDevice device = UiDevice.getInstance(getInstrumentation()); 
     UiObject allowPermissions = device.findObject(new UiSelector().text("Allow")); 
     if (allowPermissions.exists()) { 
      try { 
       allowPermissions.click(); 
      } catch (UiObjectNotFoundException e) { 
       Timber.e(e, "There is no permissions dialog to interact with "); 
      } 
     } 
    } 
} 

我发现它here

+3

请注意,这不适用于非EN语言环境或未来按钮文本更改。如果制造商定制对话框,它甚至可能会因某些设备而失败。 –

+4

奇怪的是,这不适用于LG Nexus 5与Android 6.没有找到文本“允许”。使用以下工作:新的UiSelector()。clickable(true).checkable(false).index(1) – David

+1

你如何做到这一点咖啡测试? – Bhargav

9

其实有这样做的2种方式,我知道迄今:

  1. 格兰特之前开始测试使用ADB命令(documentation)权限:

adb shell pm grant "com.your.package" android.permission.your_permission

  • 您可以单击权限对话框并使用UIAutomator设置权限(documentation)。如果您的测试是用Espresso for Android编写的,您可以轻松将Espresso和UIAutomator步骤合并到一个测试中。
  • +0

    我不明白我们如何使用'adb shell'命令以及我们的测试套件。关于点击对话框,Espresso应该能够处理它自己。问题是,在运行测试并启用权限后,下次运行测试时,它将失败,因为该设置会持续存在,并且对话框将不会再显示。 – Soflete

    +1

    Espresso无法处理与权限对话框的交互,因为dialog是其他应用程序的实例 - com.android.packageinstaller。 – denys

    +0

    您是否尝试过使用UIAutomator接受弹出窗口?对我来说,它似乎没有找到“允许”按钮。任何想法如何解决这个问题? – conca

    31

    基于@riwnodennyk的解决方案,我们实现一个更好的。该接受的答案有两个问题:

    • 如果设备是不是英文的,它不会工作
    • 如果您已经允许的权限之前,但您的应用程序已与文本的按钮“允许”,它将在每次测试中被点击,如果是权限对话框则无关紧要。

    所以,这里的最终解决方案:

    public static void allowPermissionsIfNeeded(String permissionNeeded) { 
        try { 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasNeededPermission(permissionNeeded)) { 
         sleep(PERMISSIONS_DIALOG_DELAY); 
         UiDevice device = UiDevice.getInstance(getInstrumentation()); 
         UiObject allowPermissions = device.findObject(new UiSelector() 
          .clickable(true) 
          .checkable(false) 
          .index(GRANT_BUTTON_INDEX)); 
         if (allowPermissions.exists()) { 
          allowPermissions.click(); 
         } 
         } 
        } catch (UiObjectNotFoundException e) { 
         System.out.println("There is no permissions dialog to interact with"); 
        } 
        } 
    

    发现全班同学在这里:https://gist.github.com/rocboronat/65b1187a9fca9eabfebb5121d818a3c4

    顺便说一句,因为这个答案一直是流行的,我们增加了PermissionGranter百瑞斯塔,我们上面的咖啡和UiAutomator工具,使仪器的测试绿色:https://github.com/SchibstedSpain/Barista检查出来,COS,我们将保持它通过释放释放。

    +1

    创作奇迹!也用于测试不同的语言环境,这是我的情况。 – Sloy

    +1

    太棒了!在大多数的答案中,人们不会采取护理设备的语言。所以,做得很好,谢谢! – alxsimo

    +0

    如果您还想测试拒绝权限,则可以复制该代码,但将索引更改为0。索引2是复选框,永远不会再问我。 – Ethan

    19

    试验是用类似运行之前,您可以授予权限:

    @Before 
    public void grantPhonePermission() { 
        // In M+, trying to call a number will trigger a runtime dialog. Make sure 
        // the permission is granted before running this test. 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
         getInstrumentation().getUiAutomation().executeShellCommand(
           "pm grant " + getTargetContext().getPackageName() 
             + " android.permission.CALL_PHONE"); 
        } 
    } 
    

    但你不能撤销。如果您尝试pm reset-permissionspm revoke...该进程被终止。

    +2

    我在google测试示例代码中发现了类似的代码。它适用于该软件包中的电话手机示例应用程序,但如果在应用程序启动后立即询问权限(例如相机权限),则该功能无效。任何人有任何想法为什么? –

    +0

    你可以试试@BeforeClass。 –

    +0

    @ fangmobile.com也许是因为该活动也是在@ Before规则中开始的? – nickmartens1980

    7

    您可以通过在开始测试之前授予权限轻松实现这一点。例如,如果你都应该在试运行过程中使用的摄像头,您可以授权如下

    @Before 
    public void grantPermission() { 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
         getInstrumentation().getUiAutomation().executeShellCommand(
           "pm grant " + getTargetContext().getPackageName() 
             + " android.permission.CAMERA"); 
        } 
    } 
    
    +0

    ,您应该可以使用相同的方法撤销权限,这对我很有用!我必须查看清单以准确找出使用此技术授予的权限。 – jerimiah797

    31

    随着Android Testing Support Library 1.0的新版本,有一个GrantPermissionRule,你可以在你的测试使用前授予许可开始任何测试。

    @Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION); 
    
    +0

    它在你的测试中起作用吗? –

    +0

    是的,没有任何问题。 – Niklas

    +0

    工程就像一个魅力!查看[此博客文章](http://kotlindevelopment.com/runtime-permissions-espresso-done-right/)了解更多信息。 –

    0

    我知道答案已经但是接受,而不是已经暗示了一遍又一遍,另一种更好的方法是做在实际测试中,你要对特定下面的if声明, OS版本:

    @Test 
    fun yourTestFunction() { 
        Assume.assumeTrue(Build.VERSION.SDK_INT >= 23) 
        // the remaining assertions... 
    } 
    

    如果assumeTrue函数调用评估,以虚假的表情,测试将停止和被忽略,这我假设是你想要什么,此案正在设备上执行的测试pre SDK 23.

    1

    ESPRESSO UPDATE

    这种单一的代码行授予列为立即生效授权方法参数 每权限。换句话说,应用程序 会像对待,如果权限已经授予 - 没有更多 对话框

    @Rule @JvmField 
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION) 
    

    和gradle这个

    dependencies { 
        ... 
        testImplementation "junit:junit:4.12" 
        androidTestImplementation "com.android.support.test:runner:1.0.0" 
        androidTestImplementation "com.android.support.test.espresso:espresso-core:3.0.0" 
        ... 
    } 
    

    参考:https://www.kotlindevelopment.com/runtime-permissions-espresso-done-right/

    0

    我已经实施了一个利用包装类的解决方案,覆盖并构建变体配置。解决方案需要很长时间才能解释,可以在这里找到:https://github.com/ahasbini/AndroidTestMockPermissionUtils

    现在还装在一个SDK,但主要的想法是重写的ContextWrapper.checkSelfPermissionActivityCompat.requestPermissions的功能来进行操作和返回嘲笑结果欺骗应用到不同的场景,就像进行测试:许可的情况下,因此拒绝该应用程序请求并在授予许可的情况下结束。即使应用程序一直都有权限,但这种情况也会发生,但它的想法是被重写实现的模拟结果所欺骗。

    此外,该实现还有一个名为PermissionRuleTestRule类,它可以在测试类中使用,以轻松模拟所有条件以无缝测试权限。例如,还可以确保应用程序称为requestPermissions()