Search code examples
androidandroid-jetpack-composeandroid-testing

UI Test crash using ComposeRule


I am writing a UI test that goes to a Search screen, conducts a search, and clicks on an item to get to the details of that item. I get a crash on most, but not all of the items the automation test clicks on. I can see that the data loads but crashes while waiting for idle.

Code:

composeTestRule.waitForIdle()

Crash:

Caused by: java.util.concurrent.ExecutionException: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean androidx.compose.ui.node.LayoutNode.getNeedsOnPositionedDispatch$ui_release()' on a null object reference
        at androidx.test.espresso.Espresso.onIdle(Espresso.java:35)
        at androidx.test.espresso.Espresso.onIdle(Espresso.java:21)
        at androidx.compose.ui.test.junit4.EspressoLink_androidKt.runEspressoOnIdle(EspressoLink.android.kt:92)
        at androidx.compose.ui.test.junit4.EspressoLink.runUntilIdle(EspressoLink.android.kt:79)
        at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.waitForIdle(ComposeUiTest.android.kt:308)
        at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.access$waitForIdle(ComposeUiTest.android.kt:217)
        at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.waitForIdle(ComposeUiTest.android.kt:391)
        at androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitForIdle(AndroidComposeTestRule.android.kt:177)

Update: I found out that this crashes after "focusRequester.requestFocus()" in my layout, but only if there are items in the list that requests focus. i have checked that focusRequester is not null.

Update 2: I have uncovered that running "lazyListState.scrollToItem()" when the page loads causes this crash. Seems odd to me but, if i add a delay prior to that call, it works. Still seems like a hack though.


Solution

  • Fix: In my case i was using "lazyListState.scrollToItem()" inside the "onFocusChanged" modifier, which requires a coroutine. Taking that code out to a "launchedEffect" fixed this issue

    So changing this

    .onFocusChanged { event ->
        if (event.isFocused) {
            coroutineScope.launch {
                lazyColumnState.scrollToItem(selectedItemIndex, 0)
            }
        }
    }
    

    To this

    LaunchedEffect(key1 = hasFocus) {
        if (hasFocus) {
            coroutineScope.launch {
                lazyColumnState.scrollToItem(selectedItemIndex, 0)
            }
        }
    }