Jetpack Compose 版本:Compose BOM 2023.08.00
使用的 Jetpack Compose 组件:基础、材质、动画、实时数据、UI 工具、视图模型
Kotlin 版本:10.9.10
重现步骤或重现代码示例:
将项目添加到lazyColumn,并将底部项目推到列中较低的位置时,项目节点会重复。这使得我的仪器测试失败,因为有多个节点具有相同的测试标签。预编写 BOM 2023.08.00 时没有发生这种情况
请查看随附的屏幕截图。当惰性列表项发生更改时,节点在层次结构中保持不变。如果您查看两张屏幕截图,其中一张显示了按钮所在的真实节点,我们可以看到该节点上的参数。另一个屏幕截图显示了一个虚拟节点,即按钮曾经所在的位置。它没有参数。
有没有人经历过这种情况,如果有的话,是否有解决方法可以仅在组成仪器测试时定位可见节点,而不会出现不明确的节点异常?
这是我的lazyColumn实现的一些示例代码:
LazyColumn(
modifier = Modifier.testTag(COMMUTE_ITEM_LIST),
state = state
) {
item { Spacer(modifier= Modifier.height(topPaddingDp)) }
items(itemViews.size, key = { itemViews[it].uniqueId }) { index ->
AnimateItemPlacements {
CommuteItemRow(
modifier = Modifier,
itemViews = itemViews,
index = index,
onRemoveItemAtIndex = onRemoveItemAtIndex,
onEditItemAtIndex = onEditItemAtIndex,
onItemMoved = onItemMoved
)
}
}
item {
AddItemButton(
modifier = Modifier,
index = itemViews.size,
onAddItemAtIndex = onAddItemAtIndex
)
}
item {
Spacer(Modifier.height(bottomPaddingDp))
}
}
浏览完https://issuetracker.google.com/issues/187188981后,有一个解决方法最终对我有用。
问题在于,某些节点仍然缓存在惰性列表中,并且当尝试使用如下调用挑选出特定节点时,测试现在会抛出异常:
composeTestRule.onNodeWithTag("testTag")
同时的解决方案(直到 Compose 团队修复此问题)是使用这些扩展函数,由上述链接的第 12 篇帖子中的海报提供:
/** A patched version of [ComposeTestRule.onNode] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodePatched(matcher: SemanticsMatcher) =
onNode(matcher and isNotCached())
/** A patched version of [ComposeTestRule.onNodeWithText] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithTextPatched(text: String, substring: Boolean = false, ignoreCase: Boolean = false, useUnmergedTree: Boolean = false) =
onAllNodesWithText(text = text, substring = substring, ignoreCase = ignoreCase, useUnmergedTree = useUnmergedTree)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onNodeWithTag] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithTagPatched(testTag: String, useUnmergedTree: Boolean = false) =
onAllNodesWithTag(testTag = testTag, useUnmergedTree = useUnmergedTree)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onNodeWithContentDescription] that filters out cached views from lazy views. */
fun ComposeTestRule.onNodeWithContentDescriptionPatched(label: String) =
onAllNodesWithContentDescription(label = label)
.filterToOneNotCached()
/** A patched version of [ComposeTestRule.onAllNodesWithText] that filters out cached views from lazy views. */
fun ComposeTestRule.onAllNodesWithTextPatched(text: String) =
onAllNodesWithText(text = text)
.filterOutCached()
/** A patched version of [ComposeTestRule.onAllNodesWithContentDescription] that filters out cached views from lazy views. */
fun ComposeTestRule.onAllNodesWithContentDescriptionPatched(label: String) =
onAllNodesWithContentDescription(label = label)
.filterOutCached()
/** Filters out cached views from lazy views and expects 1 or 0 results. */
fun SemanticsNodeInteractionCollection.filterToOneNotCached() =
filterToOne(isNotCached())
/** Filters out cached views from lazy views and expects 0 or more results. */
fun SemanticsNodeInteractionCollection.filterOutCached() =
filter(isNotCached())
/**
* Matches against only nodes that are not "cached" by lazy lists. This allows us to filter out
* nodes that do not appear in the UI but are reported by Compose's testing framework. These cached
* nodes cause issues because they will cause assertIsDisplayed to fail due to 2 nodes with the same
* values are reportedly displayed, but one is displayed and the other is cached. Cached nodes also
* cause assertDoesNotExist to fail because the cached node that does not exist on the UI does exist
* according to the test framework.
*
* This matcher is used in the methods above to globally filter out these very unexpected nodes.
* We hope Compose stops reporting these cached nodes in a future update and we can remove this patch.
*
* https://issuetracker.google.com/issues/187188981
*/
fun isNotCached() = SemanticsMatcher("isNotCached") { node -> node.layoutInfo.isPlaced }