Espresso - 单击对话框的按钮
Espresso - click on the button of the dialog
想测试一下Android6的权限,但是没找到用Espresso点击"Allow"按钮的方法。有办法吗?
Espresso 的版本是 2.2.1。
测试:
@Test
public void acceptFirstPermission() throws Exception {
onView(withText("ALLOW")).perform(click());
}
行为:
I still receiving the frozen screen with the Dialog (like on the screenshot). Test executing all time until he becomes finished.
输出:
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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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
好吧,它可以像
一样简单
onView(withText("Allow")).perform(click());
当然这不是一个完美的解决方案,所以有两种方法可以让它更健壮:要么使用 hierarchyviewer 分析应用程序以找到有关如何识别按钮的提示(例如内容描述),要么深入研究进入提示的 Android 源代码(例如按钮的资源 ID)。
编辑
嗯,没那么简单。我写了 an article 关于使用 UiAutomator 来完成这项工作。
我看到您正在尝试测试应用权限。我认为用 Espresso 测试它可能几乎是不可能的。您可能需要使用另一个名为 uiatomator
.
的 UI 测试工具执行此操作
uiatomator
,由 Google 制作的另一个很棒的工具允许您测试 Android 系统功能,如通知和屏幕锁定。您可以将它与 Espresso 测试框架一起使用。
要查找更多信息,请阅读这篇文章:
http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html
和 uiautomator 文档,您会找到 here.
下面的代码片段将在您 运行 测试之前授予权限。希望它能解决您的问题:)
@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");
}
}
您可以使用动态方式接受权限,如果对话框出现,将授予权限,如果不出现,则不会抛出任何错误
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();
}
}
}
}
如果您将 UI-Automator 与 AndroidX 一起使用,您可以找到对话框和按钮。
这是一个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()
}
我刚刚遇到了同样的问题,需要动态允许或拒绝权限,但需要能够找到按钮以在不使用字符串的情况下单击权限对话框(因为我们将 运行多种其他语言以及英语)。
我曾为 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 测试。
已编辑:已更新以处理用户第一次拒绝请求(没有复选框),第二次出现复选框的情况。
想测试一下Android6的权限,但是没找到用Espresso点击"Allow"按钮的方法。有办法吗?
Espresso 的版本是 2.2.1。
测试:
@Test
public void acceptFirstPermission() throws Exception {
onView(withText("ALLOW")).perform(click());
}
行为:
I still receiving the frozen screen with the Dialog (like on the screenshot). Test executing all time until he becomes finished.
输出:
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.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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
好吧,它可以像
一样简单onView(withText("Allow")).perform(click());
当然这不是一个完美的解决方案,所以有两种方法可以让它更健壮:要么使用 hierarchyviewer 分析应用程序以找到有关如何识别按钮的提示(例如内容描述),要么深入研究进入提示的 Android 源代码(例如按钮的资源 ID)。
编辑
嗯,没那么简单。我写了 an article 关于使用 UiAutomator 来完成这项工作。
我看到您正在尝试测试应用权限。我认为用 Espresso 测试它可能几乎是不可能的。您可能需要使用另一个名为 uiatomator
.
uiatomator
,由 Google 制作的另一个很棒的工具允许您测试 Android 系统功能,如通知和屏幕锁定。您可以将它与 Espresso 测试框架一起使用。
要查找更多信息,请阅读这篇文章:
http://qathread.blogspot.com/2015/05/espresso-uiautomator-perfect-tandem.html
和 uiautomator 文档,您会找到 here.
下面的代码片段将在您 运行 测试之前授予权限。希望它能解决您的问题:)
@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");
}
}
您可以使用动态方式接受权限,如果对话框出现,将授予权限,如果不出现,则不会抛出任何错误
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();
}
}
}
}
如果您将 UI-Automator 与 AndroidX 一起使用,您可以找到对话框和按钮。
这是一个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()
}
我刚刚遇到了同样的问题,需要动态允许或拒绝权限,但需要能够找到按钮以在不使用字符串的情况下单击权限对话框(因为我们将 运行多种其他语言以及英语)。
我曾为 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 测试。
已编辑:已更新以处理用户第一次拒绝请求(没有复选框),第二次出现复选框的情况。