浓缩咖啡 - 点击对话框中的按钮

9

我想测试Android 6的权限,但是我没有找到使用Espresso点击“允许”按钮的方法。有没有办法做到这一点?

enter image description here

Espresso的版本是2.2.1。

测试内容:

    @Test
    public void acceptFirstPermission() throws Exception {
        onView(withText("ALLOW")).perform(click());
    }

行为:

我仍然会收到带有对话框的冻结屏幕(就像截图上的那样)。测试一直执行,直到完成为止。

输出:

Running tests
Test running started
android.support.test.espresso.NoActivityResumedException: No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:580)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:82)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:53)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
at com.walletsaver.app.test.espresso.MarshmallowPermissionsTest.acceptFirstPermission(MarshmallowPermissionsTest.java:31)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at android.support.test.internal.statement.UiThreadStatement.evaluate(UiThreadStatement.java:55)
at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:257)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:240)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)

Finish

可能是Android Marshmallow:使用Espresso测试权限?的重复问题。 - Roc Boronat
6个回答

9
我看到你正在尝试测试应用权限。我认为使用Espresso进行测试可能相当困难。您可能需要使用另一种名为uiatomator的UI测试工具来执行此操作。 uiatomator是由谷歌开发的另一个优秀工具,可用于测试Android系统功能,例如通知和屏幕锁定。您可以将其与Espresso测试框架一起使用。
要获取更多信息,请阅读本文:

http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html

还有uiautomator文档,你可以在这里找到。


1
UI Automator与权限对话框完美配合。谢谢! - Val

7

好的,它可以很简单

onView(withText("Allow")).perform(click());

当然,这并不是一个完美的解决方案,因此有两种方法可以使其更加稳健:使用层次结构查看器(hierarchyviewer)分析应用程序以找到识别按钮的提示(例如内容描述),或者深入研究Android源代码以获取提示(例如按钮的资源ID)。
编辑
嗯,事情并不那么简单。我写了一篇关于如何使用UiAutomator使其工作的文章

很遗憾,它不起作用。我仍然收到带有对话框的冻结屏幕(就像问题中的截图)。测试一直执行,直到完成为止。 - Val
请问您能否更新一下您的问题,附上测试代码?通常情况下,如果测试无法通过文本找到按钮,它就会失败。 - Egor
你是否忘记调用getActivity()方法,正如测试结果所示? - Egor
实际上,Activity是可见的并且正在等待操作。我不明白你建议如何调用test.getActivity()。你能更新你的答案并提供代码示例吗? - Val
Espresso无法与警报交互,仅限于应用程序本身。 - juhlila

1
您可以使用动态方式接受权限,如果弹出对话框,则会授予权限;如果未弹出,则不会抛出任何错误。
 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) {
                    e.printStackTrace();
                }
            }
        }
    }

0

我遇到了同样的问题,需要动态地允许或拒绝权限,但需要能够找到在许可对话框上单击的按钮,而不使用字符串(因为我们将在其他许多语言以及英语中运行)。

我曾尝试查找Android使用的Allow和Deny字符串的常量,但找不到它们,因此采用了有些复杂的选项,即浏览布局以找到在权限对话框中显示应用程序名称的布局(例如,在这种情况下,“WalletSaver”),然后转到对话框的父布局,接着深入到我想要的其他UI元素。注意:APP_NAME只是您的应用程序名称弹出对话框中显示的用户可见字符串(因为您控制应用程序的名称和是否翻译) - 在这种情况下是“WalletSaver debug”。

protected fun clickPermissionDeny() {
    var dev = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    var deny : UiObject2
    if (dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].childCount == 1) { // If there is no "don't ask again"
        deny = dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].children[0].children[1].children[0]
    } else { // Else move past checkbox
        deny = dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].children[1].children[0].children[0]
    }
    deny.click();
}

protected fun clickPermissionAllow() {
    var dev = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    var allow : UiObject2
    while(dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.childCount<2)
        Thread.sleep(100);
    if (dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].childCount == 1) { // If there is no "don't ask again"
        allow = dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].children[0].children[1].children[1]
    } else { // Else move past checkbox
        allow = dev.findObjects(By.textContains(APP_NAME))[0].parent.parent.parent.children[1].children[1].children[1].children[1]
    }
    allow.click()
}

虽然不太美观,但它可以用于跨多种语言的UI测试,希望如此。

编辑:更新以处理用户第一次拒绝请求的情况(没有复选框),第二次出现复选框。


0

如果您在AndroidX中使用UI-Automator,可以找到对话框和按钮。

这是一个gradle依赖项代码。

dependencies {
    androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}

使用以下代码可以访问ALLOW按钮。

这是Kotlin代码。

val button = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
                .findObject(
                    UiSelector().text(
                        activityTestRule
                            .activity
                            .getString(R.string.allow_button)
                            .toUpperCase()
                    )
                )

if (button.exists() && button.isEnabled) {
    button.click()
}

-3
以下代码片段将在运行测试之前授予权限。希望它能解决你的问题 :)
 @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.
        // goo.gl/C8T4BU
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getInstrumentation().getUiAutomation().executeShellCommand(
                    "pm grant " + getTargetContext().getPackageName()
                            + " android.permission.READ_PHONE_STATE"
                            + " android.permission.PHONE");
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接