Jetpack Compose version: Compose BOM 2023.08.00
Jetpack Compose版本:Compose BOM 2023.08.00
Jetpack Compose component(s) used: Foundation, material, animations, livedata, ui-tooling, viewmodel
使用的Jetpack组合组件(S):基础、材质、动画、实况数据、UI工具、视图模型
Kotlin version: 1.9.10
Kotlin版本:1.9.10
Steps to Reproduce or Code Sample to Reproduce:
复制步骤或编码要复制的样本:
When adding an item to a lazyColumn, and pushing a bottom item lower in the column, the item node is duplicated. This is making my instrumented tests fail because there are multiple nodes with the same test tag. This did not happen pre-compose BOM 2023.08.00
将项添加到lazyColumn并将列中底部的项推低时,项节点将被复制。这会导致我的检测测试失败,因为有多个节点具有相同的测试标记。这在合成BOM 2023.08.00之前没有发生
Please view the attached screenshots. Nodes remain persistent in the hierarchy when the lazy list items change. If you look at both screenshots, one of them shows the real node where the button is present, and we can see parameters on the node. The other screenshot shows a phantom node, where the button used to be. It does not have parameters.
请查看附件中的截图。当懒惰列表项更改时,节点在层次结构中保持持久。如果您查看两个屏幕截图,其中一个显示了按钮所在的实际节点,我们可以看到该节点上的参数。另一个屏幕截图显示了一个虚拟节点,按钮曾经位于该节点的位置。它没有参数。
Has anyone experienced this and if so are there workarounds to target visible nodes only in compose instrumented tests, without getting ambiguous node exceptions?
有没有人经历过这种情况?如果有,有没有解决办法,只在编写指令插入的测试中针对可见节点,而不会得到模棱两可的节点异常?
Here is some sample code of my lazyColumn implementation:
下面是我的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))
}
}
Link to issue tracker:
https://issuetracker.google.com/issues/299304333
问题跟踪器链接:https://issuetracker.google.com/issues/299304333
更多回答
Have you seen this issue? It seems to describe your problem, correct?
你看过这期杂志吗?这似乎描述了你的问题,对吗?
@ianhanniballake Yes, this seems like the exact issue. The workaround in post #12 seems to offer an unclean but good enough solution for now!
@ianhanniballake是的,这看起来就是这个问题。POST#12中的解决办法似乎提供了一个不干净但目前足够好的解决方案!
优秀答案推荐
After looking through https://issuetracker.google.com/issues/187188981, there is a workaround that ended up working for me.
在浏览了https://issuetracker.google.com/issues/187188981,之后,有一个变通办法最终对我起作用了。
The issue is that some nodes remain cached in lazy lists and tests now throw exceptions when trying to single out a specific node with calls like this:
问题是,一些节点仍然缓存在惰性列表中,现在,当尝试使用如下调用挑选特定节点时,测试会抛出异常:
composeTestRule.onNodeWithTag("testTag")
The workaround solution in the meantime (until the Compose team fixes this issue) is to use these extension functions, courtesy of the poster in post #12 of the above link:
在此期间(直到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 }
更多回答
我是一名优秀的程序员,十分优秀!