Android testing: Waited for the root of the view hierarchy to have window focus Android testing: Waited for the root of the view hierarchy to have window focus android android

Android testing: Waited for the root of the view hierarchy to have window focus


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.

Android system dialog: Unfortunately, Launcher has stopped.

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:

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 "Application Isn't Responding. Do you want to close it? [Wait] [OK]":

Android system dialog: 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, they 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 UIAutomator 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

@BeforeClasspublic 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 Rightsleep 3; adb shell input keyevent 22# Send keystroke Arrow Right againsleep 3; adb shell input keyevent 22# Send keystroke Enter to press a button on the dialogsleep 3; adb shell input keyevent 66

More info:


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()));


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.