androidx.compose.ui.test.junit4.android.ComposeNotIdleExcept



我正在尝试制作一个每个屏幕都有不同顶部应用程序栏的compose应用程序。我的策略是通过每个屏幕为应用程序栏的数据传递一个状态设置器。然而,传入onClick lambda会破坏我的测试。

这里有一个SSCCE:

// ActionState.kt
class ActionState(val title: String = "", val onClick: () -> Unit = {}) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ActionState
if (title != other.title) return false
// Commenting out this line fixes the test but breaks the implementation
if (onClick != other.onClick) return false
return true
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + onClick.hashCode()
return result
}
}
// MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
@Composable
fun TopBar(actionState: ActionState) {
TopAppBar(
title = { Text("App") },
actions = {
val contentColor = contentColorFor(MaterialTheme.colors.primarySurface)
val colors = ButtonDefaults.textButtonColors(contentColor = contentColor)
TextButton(onClick = actionState.onClick, colors = colors) {
Text(actionState.title)
}
},
)
}
@Composable
fun ScreenA(setActionState: (ActionState) -> Unit, navigateToScreenB: () -> Unit) {
setActionState(ActionState("To B", navigateToScreenB))
Text("Screen A")
}
@Composable
fun ScreenB(setActionState: (ActionState) -> Unit, navigateToScreenA: () -> Unit) {
setActionState(ActionState("To A", navigateToScreenA))
Text("Screen B")
}
@Composable
fun App() {
val navController = rememberNavController()
val (actionState, setActionState) = remember { mutableStateOf(ActionState()) }
Scaffold(topBar = { TopBar(actionState = actionState) }) {
NavHost(navController = navController, startDestination = "a") {
composable("a") {
ScreenA(
setActionState = setActionState,
navigateToScreenB = { navController.navigate("b") },
)
}
composable("b") {
ScreenB(
setActionState = setActionState,
navigateToScreenA = { navController.navigate("a") },
)
}
}
}
}
// Test
class ExampleInstrumentedTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun test() {
composeTestRule.onNodeWithText("Screen A").assertIsDisplayed()
composeTestRule.onNodeWithText("To B").performClick()
composeTestRule.onNodeWithText("Screen B").assertIsDisplayed()
}
}

我在测试中得到的错误是:

androidx.compose.ui.test.junit4.android.ComposeNotIdleException: Idling resource timed out: possibly due to compose being busy.
IdlingResourceRegistry has the following idling resources registered:
- [busy] androidx.compose.ui.test.junit4.android.ComposeIdlingResource@408168
All registered idling resources: Compose-Espresso link
at androidx.compose.ui.test.junit4.android.EspressoLink_androidKt.rethrowWithMoreInfo(EspressoLink.android.kt:135)
at androidx.compose.ui.test.junit4.android.EspressoLink_androidKt.runEspressoOnIdle(EspressoLink.android.kt:109)
at androidx.compose.ui.test.junit4.android.EspressoLink.runUntilIdle(EspressoLink.android.kt:78)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule.waitForIdle(AndroidComposeTestRule.android.kt:289)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule.access$waitForIdle(AndroidComposeTestRule.android.kt:155)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$AndroidTestOwner.getRoots(AndroidComposeTestRule.android.kt:441)
at androidx.compose.ui.test.TestContext.getAllSemanticsNodes$ui_test_release(TestOwner.kt:95)
at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNodes$ui_test_release(SemanticsNodeInteraction.kt:79)
at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrDie(SemanticsNodeInteraction.kt:145)
at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNode(SemanticsNodeInteraction.kt:96)
at androidx.compose.ui.test.AndroidAssertions_androidKt.checkIsDisplayed(AndroidAssertions.android.kt:29)
at androidx.compose.ui.test.AssertionsKt.assertIsDisplayed(Assertions.kt:33)
at ogbe.eva.topbarbug.ExampleInstrumentedTest.test(ExampleInstrumentedTest.kt:16)
... 33 trimmed
Caused by: androidx.test.espresso.IdlingResourceTimeoutException: Wait for [Compose-Espresso link] to become idle timed out
at androidx.test.espresso.IdlingPolicy.handleTimeout(IdlingPolicy.java:4)
at androidx.test.espresso.base.UiControllerImpl$5.resourcesHaveTimedOut(UiControllerImpl.java:1)
at androidx.test.espresso.base.IdlingResourceRegistry$Dispatcher.handleTimeout(IdlingResourceRegistry.java:4)
at androidx.test.espresso.base.IdlingResourceRegistry$Dispatcher.handleMessage(IdlingResourceRegistry.java:6)
at android.os.Handler.dispatchMessage(Handler.java:102)
at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:14)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:8)
at androidx.test.espresso.base.UiControllerImpl.loopMainThreadUntilIdle(UiControllerImpl.java:17)
at androidx.test.espresso.Espresso$1.run(Espresso.java:1)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

如果我从相等测试中删除onClick lambda,那么测试就通过了。但当我离开屏幕导航回来时,这个动作在应用程序中就不起作用了。我认为它最终会使用过时的数据。

我认为这个问题与remembermutableStateOf中的相等性检查有关,但我对这些方法了解不够,无法真正理解发生了什么

如何防止测试空转?或者,有没有更好的方法来实现每个屏幕顶部的应用程序栏状态?

我将脚手架移动到每个单独的屏幕中。我没有注意到顶部的应用程序栏闪烁,所以他们可能已经修复了导致这种情况的错误。

最新更新