'Android testing: Waited for the root of the view hierarchy to have window focus
In Android Ui testing, I want to click on a spinner item in a dialog, but it pop up with this error:
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)
I have tried
onData(allOf(is(instanceOf(String.class)),containsString("A4"))).inRoot(isPlatformPopup()).perform(click());
and
onView(withText(containsString("A4"))).inRoot(isFocusable()).check(matches(isDisplayed()));
and
onView(withText(containsString("A4"))).inRoot(withDecorView(not(getActivity().getWindow().getDecorView()))).check(matches(isDisplayed()));
but none of them works... Can anyone tell me how to get the ralavant root please?
Solution 1:[1]
This error can happen when a system dialog is displayed — like "Power Off"
or "Unfortunately, Launcher has stopped"
(a background app crashed) — and you try to run an Espresso unit test whilst that dialog is visible.
Image credit: Android 4.0 emulator always has a crashing Launcher?
You can workaround it in code by dismissing the system dialog before running the test:
// 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));
Or send the broadcast on the command line using adb:
adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS
Another cause of the error is when a background app is frozen (ANR) or is running slowly, and a system dialog appears saying "Launcher isn't responding. Do you want to close it? [Wait] [OK]"
:
Image credit: https://engineering.linkedin.com/blog/2016/08/introducing-and-open-sourcing-test-butler--reliable-android-test
If you try to run an Espresso test while this dialog is visible, the tests will all fail and show the "Waited for the root..." error. There's no easy way to close this dialog programmatically. Espresso cannot click these buttons, for the reasons described here: Dismiss Alert Dialog in Android Espresso Test . However, one way is to use UI Automator to press the "Wait" button in the dialog, just before a test starts:
app/build.gradle
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
}
ActivityUiTest.kt
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()
}
}
}
ActivityUiTest.java
@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();
}
}
Alternatively, you could use adb on the command line to send screen taps or key strokes to dismiss it. For example:
# 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
or
# 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
More info:
Solution 2:[2]
I had the same error, when I used a Spinner inside a DialogFragment. This is the only code that was working for me:
onView(withText(containsString("A4"))).inRoot(isPlatformPopup()).check(matches(isDisplayed()));
Solution 3:[3]
I had similar issue when a dialog popup contains spinner items (dropdown list), my click couldn't perform on any of the spinner items and got the same error. I found a solution by using onData() method with RootMatchers:
onData(anything()).inRoot(RootMatchers.isPlatformPopup()).atPosition(1).perform(click());
Note that, the index value in atPosition() is the item's index value from the spinner list.
Solution 4:[4]
Just in case if it happens to anyone's Travis build (with exact the same log). Please check this.
Had exact the same problem, and solved by creating avd with lower target version (19).
What I've tried and didn't work:
Adding a
unlockScreen()
@Before
method to UI test.Adding / Removing
adb shell input keyevent 82 &
.Removing different
emulator
command options-no-skin
or-no-audio
or-no-window
. Now I have-no-window
there which is fine.
Finally, changing from
echo no | android create avd --force -n test -t android-24 --abi armeabi-v7a
to
echo no | android create avd --force -n test -t android-19 --abi armeabi-v7a
perfectly solve the issue.
Solution 5:[5]
I encountered this issue periodically when running Espresso tests on emulators in CI. Because the checks fail if a single test fails, the answer at https://stackoverflow.com/a/54203607/4520965 was insufficient for my use case. That answer handles dismissing the ANR Dialogue in the subsequent test class, but the test class where the ANR initially appeared would still fail. To get around this I added the following to my base ui test class that all my Espresso tests inherited from:
//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()
}
Solution 6:[6]
just update the build tools in the travis.yml:
- In this file
.travis.yml
: you should have thisbefore_install: - echo yes | android update sdk --all --filter build-tools-26.0.1 --no-ui --force
- as wel this in
.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 &
Solution 7:[7]
Maybe the problem is newly up to date, but with the new Android version.
AndroidX Test: 1.3.0
Espresso: 3.3.0
Android OS: Android 11 (API 30)
See https://github.com/android/android-test/issues/751
Solution 8:[8]
For me, when testing my custom AlertDialog
, using the .inRoot(isDialog())
method instead of .inRoot(isPlatformPopup())
worked.
onView(withText(containsString("A4"))).inRoot(isDialog()).check(matches(isDisplayed()));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | StefanTo |
Solution 3 | Jamil Rahman |
Solution 4 | Jing Li |
Solution 5 | James Jordan Taylor |
Solution 6 | denis_lor |
Solution 7 | t0m |
Solution 8 | Kes Walker |