在 Android Ui 测试中,我想点击对话框中的微调项,但弹出此错误:
va.lang.RuntimeException: Waited for the root of the view hierarchy to have window focus and not be requesting layout for over 10 seconds. If you specified a non default root matcher, it may be picking a root that never takes focus. Otherwise, something is seriously wrong. Selected Root:
Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
. All Roots:
Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@21b23506, has-window-focus=true, layout-params-type=1002, layout-params-string=WM.LayoutParams{(310,600)(722x480) gr=#10000033 sim=#1 ty=1002 fl=#1860200 fmt=-3 wanim=0x10302db surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=PopupViewContainer{id=-1, visibility=VISIBLE, width=722, height=480, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
Root{application-window-token=android.view.ViewRootImpl$W@3c913e1, window-token=android.view.ViewRootImpl$W@3c913e1, has-window-focus=false, layout-params-type=2, layout-params-string=WM.LayoutParams{(0,0)(wrapxwrap) gr=#11 sim=#20 ty=2 fl=#1800002 pfl=0x8 fmt=-3 wanim=0x1030462 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x10}, decor-view-string=DecorView{id=-1, visibility=VISIBLE, width=1136, height=1058, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
Root{application-window-token=android.view.ViewRootImpl$W@2dac97c7, window-token=android.view.ViewRootImpl$W@2dac97c7, has-window-focus=false, layout-params-type=1, layout-params-string=WM.LayoutParams{(0,0)(fillxfill) sim=#10 ty=1 fl=#81810100 pfl=0x8 wanim=0x1030461 surfaceInsets=Rect(0, 0 - 0, 0) mwfl=0x0}, decor-view-string=MultiPhoneDecorView{id=-1, visibility=VISIBLE, width=1600, height=2560, has-focus=true, has-focusable=true, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}}
at android.support.test.espresso.base.RootViewPicker.get(RootViewPicker.java:99)
at android.support.test.espresso.ViewInteractionModule.provideRootView(ViewInteractionModule.java:69)
at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:23)
at android.support.test.espresso.ViewInteractionModule_ProvideRootViewFactory.get(ViewInteractionModule_ProvideRootViewFactory.java:9)
at android.support.test.espresso.base.ViewFinderImpl.getView(ViewFinderImpl.java:68)
at android.support.test.espresso.ViewInteraction$1.run(ViewInteraction.java:120)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6117)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
我试过了
onData(allOf(is(instanceOf(String.class)),containsString("A4"))).inRoot(isPlatformPopup()).perform(click());
和
onView(withText(containsString("A4"))).inRoot(isFocusable()).check(matches(isDisplayed()));
和
onView(withText(containsString("A4"))).inRoot(withDecorView(not(getActivity().getWindow().getDecorView()))).check(matches(isDisplayed()));
但它们都不起作用... 谁能告诉我如何获得 ralavant 根?
当显示系统对话框时可能会发生此错误——例如
"Power Off"
或 "Unfortunately, Launcher has stopped"
(后台应用程序崩溃)——并且您尝试在该对话框可见时运行 Espresso 单元测试。
图片来源:Android 4.0 模拟器总是有崩溃的启动器?
您可以通过在运行测试之前关闭系统对话框来在代码中解决它:
// Use the 'testing' Context
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
// Alternative: Use the Activity for the Context
MyActivity activityUnderTest = activityTestRule.getActivity();
activityUnderTest.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
或者使用 adb 在命令行上发送广播:
adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS
错误的另一个原因是当后台应用程序被冻结(ANR)或运行缓慢时,系统对话框会出现
"Launcher isn't responding. Do you want to close it? [Wait] [OK]"
:
如果您尝试在该对话框可见时运行 Espresso 测试,测试将全部失败并显示“等待 root...”错误。没有简单的方法以编程方式关闭此对话框。 Espresso 无法点击这些按钮,原因如下:Android Espresso Test 中的关闭警报对话框。但是,一种方法是使用 UI Automator 在测试开始之前按下对话框中的“等待”按钮:
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
}
companion object {
@JvmStatic
@BeforeClass
fun dismissANRSystemDialog() {
val device = UiDevice.getInstance(getInstrumentation())
// If running the device in English Locale
var waitButton = device.findObject(UiSelector().textContains("wait"))
if (waitButton.exists()) {
waitButton.click()
}
// If running the device in Japanese Locale
waitButton = device.findObject(UiSelector().textContains("待機"))
if (waitButton.exists()) {
waitButton.click()
}
}
}
@BeforeClass
public static void dismissANRSystemDialog() throws UiObjectNotFoundException {
UiDevice device = UiDevice.getInstance(getInstrumentation());
// If the device is running in English Locale
UiObject waitButton = device.findObject(new UiSelector().textContains("wait"));
if (waitButton.exists()) {
waitButton.click();
}
// If the device is running in Japanese Locale
waitButton = device.findObject(new UiSelector().textContains("待機"));
if (waitButton.exists()) {
waitButton.click();
}
}
或者,您可以在命令行上使用 adb 发送 屏幕点击 或 击键 将其关闭。例如:
# On a 320x480 screen, click at screen location [x=233,y=293] to tap an "OK" dialog button.
# Just in case there is a "Launcher isn't responding" system dialog.
adb shell input tap 233 293
或
# Send keystroke Arrow Right
sleep 3; adb shell input keyevent 22
# Send keystroke Arrow Right again
sleep 3; adb shell input keyevent 22
# Send keystroke Enter to press a button on the dialog
sleep 3; adb shell input keyevent 66
更多信息:
当我在 DialogFragment 中使用 Spinner 时,我遇到了同样的错误。 这是唯一对我有用的代码:
onView(withText(containsString("A4"))).inRoot(isPlatformPopup()).check(matches(isDisplayed()));
当对话框弹出窗口包含微调项(下拉列表)时,我遇到了类似的问题,我的点击无法在任何微调项上执行并出现相同的错误。 我通过将 onData() 方法与 RootMatchers 一起使用找到了解决方案:
onData(anything()).inRoot(RootMatchers.isPlatformPopup()).atPosition(1).perform(click());
请注意,atPosition() 中的索引值是微调列表中项目的索引值。
以防万一它发生在任何人的 Travis 构建中(具有完全相同的日志)。请检查this.
有完全相同的问题,并通过 creating avd with lower target version (19).
我尝试过但没有奏效的:
在 UI 测试中添加一个
unlockScreen()
@Before
方法。添加/删除
adb shell input keyevent 82 &
.删除不同的
emulator
命令选项-no-skin
或-no-audio
或-no-window
。现在我有-no-window
那很好。最后,改变 来自
echo no | android create avd --force -n test -t android-24 --abi armeabi-v7a
到
echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a
完美解决问题
对我来说,在测试我的自定义
AlertDialog
时,使用.inRoot(isDialog())
方法而不是.inRoot(isPlatformPopup())
有效。
onView(withText(containsString("A4"))).inRoot(isDialog()).check(matches(isDisplayed()));
我在 CI 的模拟器上运行 Espresso 测试时定期遇到这个问题。因为如果单个测试失败,检查就会失败,所以 https://stackoverflow.com/a/54203607/4520965 上的答案对于我的用例来说是不够的。该答案处理在后续测试类中取消 ANR 对话,但最初出现 ANR 的测试类仍然会失败。为了解决这个问题,我将以下内容添加到我的所有 Espresso 测试继承自的基本 ui 测试类中:
//Running count of the number of Android Not Responding dialogues to prevent endless dismissal.
private var AnrCount = 0
//`RootViewWithoutFocusException` class is private, need to match the message (instead of using type matching).
private val rootViewWithoutFocusExceptionMsg = java.lang.String.format(
Locale.ROOT,
"Waited for the root of the view hierarchy to have "
+ "window focus and not request layout for 10 seconds. If you specified a non "
+ "default root matcher, it may be picking a root that never takes focus. "
+ "Root:")
@Before
fun setUpHandler() {
Espresso.setFailureHandler { error, viewMatcher ->
if (error.message!!.contains(rootViewWithoutFocusExceptionMsg) && AnrCount < 3) {
AnrCount++
handleAnrDialogue()
} else { // chain all failures down to the default espresso handler
DefaultFailureHandler(context).handle(error, viewMatcher)
}
}
}
private fun handleAnrDialogue() {
val device = UiDevice.getInstance(getInstrumentation())
// If running the device in English Locale
val waitButton = device.findObject(UiSelector().textContains("wait"))
if (waitButton.exists()) waitButton.click()
}
只需更新 travis.yml 中的构建工具:
.travis.yml
:你应该有这个before_install: - echo yes | android update sdk --all --filter build-tools-26.0.1 --no-ui --force
.travis.yml
中的这个:script: echo no | android create avd --force -n test -t android-22
--abi armeabi-v7a emulator -avd test -no-audio -no-window &
android-wait-for-emulator
adb shell settings put global window_animation_scale 0 &
adb shell settings put global transition_animation_scale 0 &
adb shell settings put global animator_duration_scale 0 &
adb shell input keyevent 82 &
也许问题是最新的,但是新的Android版本。
AndroidX 测试:1.3.0
浓缩咖啡:3.3.0
安卓操作系统:安卓 11(API 30)
见https://github.com/android/android-test/issues/751
我已经通过我自己在 Ultron 框架中的解决方案解决了这个问题(你可以在那里检查实现)。你可以在这里找到答案:https://github.com/open-tool/ultron/wiki/Avoiding-nontrivial-element-lookup-exceptions