自 Compose BOM 2023.08.00 以来,仪器测试失败,因为惰性列表更改似乎在层次结构中留下了旧节点

问题描述 投票:0回答:1

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/299304333

android android-studio android-jetpack-compose android-espresso android-jetpack
1个回答
0
投票

浏览完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 }
© www.soinside.com 2019 - 2024. All rights reserved.