Espresso - 检查特定图像的 recyclerview 位置

Espresso - checking recyclerview position for specific image

我很难弄清楚如何使用 espresso 测试某个图像是否显示在 recyclerview 中的某个位置。我有一个人的列表,当其中一个被选中时,我会在 recyclerview 中他的图像周围显示一个选定的指示器。所以我想检查,例如,位置 0 是否显示此指示器。我正在尝试的是:

fun test(position: Int, @DrawableRes res: Int): ViewInteraction {
    return onView(withId(recyclerViewId)).check(matches(hasItemAtPosition(position, hasBackground(res))))
}

 private fun hasItemAtPosition(position: Int, matcher: Matcher<View>): Matcher<View> {
    return object : BoundedMatcher<View, RecyclerView>(RecyclerView::class.java) {

        override fun describeTo(description: Description?) {
            description?.appendText("has item at position $position : ")
            matcher.describeTo(description)
        }

        override fun matchesSafely(recyclerView: RecyclerView): Boolean {
            val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
                ?: return false
            return matcher.matches(viewHolder.itemView)
        }
    }
}

如果我使用 withText 而不是 withBackground 并匹配项目的文本,此代码工作正常。

我收到的错误如下所示:

androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'has item at position 0 : has background with drawable ID: 2131231310' doesn't match the selected view.
    Expected: has item at position 0 : has background with drawable ID: 2131231310

我对意式浓缩咖啡和一般测试有点陌生,所以希望有人有任何建议。

更新:

测试方法在自定义中class我看起来像这样:

class RecyclerViewWait(@IdRes val recyclerViewId: Int) {

test()
hasItemAtPosition()
}

所以这两个方法也在那里(上面的那些)

我从另一个 class 给他们打电话,像这样:

override fun doesPositionContainImageInList(position: Int, imageRes: Int): ViewInteraction {
    return RecyclerViewWait(R.id.recyclerViewTest).checkBackground(position, imageRes)

return 中的哪个是从我的机器人 class 中调用的,如下所示:

fun isImageShown(): Boolean {
    return viewFinder.doesPositionContainImageInList(
        0,
        R.drawable.ic_selected_avatar
    ).isDisplayed()
}

我希望这是有道理的。

您的自定义匹配器匹配hasBackgroundviewHolder.itemView,这是一个根视图适配器项,意味着如果根视图的背景不匹配,匹配器将失败。

如果其中一个子视图包含背景,那么也许您应该用 hasDescendant 包装匹配器,这允许匹配器匹配项目中的所有子视图:

val hasDescendantWithBackground = hasDescendant(hasBackground(res))
onView(withId(recyclerViewId)).check(matches(
    hasItemAtPosition(position, hasDescendantWithBackground)))

此外,如果位置在屏幕外(视图尚未布局),findViewHolderForAdapterPosition 可能无法在您的匹配器中正常工作。

我的解决方案最终是这样的:

fun checkBackground(position: Int, @DrawableRes imageRes: Int): ViewInteraction {
    return onView(withId(recyclerViewId))
        .check(matches(hasItemAtPosition(position, hasDescendant(withDrawable(imageRes)))))
}

private fun hasItemAtPosition(position: Int, matcher: Matcher<View>): Matcher<View> {
    return object : BoundedMatcher<View, RecyclerView>(RecyclerView::class.java) {

        override fun describeTo(description: Description?) {
            description?.appendText("has item at position $position : ")
            matcher.describeTo(description)
        }

        override fun matchesSafely(recyclerView: RecyclerView): Boolean {
            val viewHolder = recyclerView.findViewHolderForAdapterPosition(position)
                ?: return false
            return matcher.matches(viewHolder.itemView)
        }
    }
}

private fun withDrawable(@DrawableRes resourceId: Int): Matcher<View> {
    return DrawableMatcher(resourceId)
}

private class DrawableMatcher(private val expectedId: Int) : TypeSafeMatcher<View>() {

    @Suppress("ReturnCount")
    override fun matchesSafely(target: View): Boolean {
        if (target !is ImageView || target.visibility == View.GONE) {
            return false
        }
        if (expectedId < 0) {
            return target.drawable == null
        }
        val resources = target.context.resources
        val expectedDrawable: Drawable =
            ResourcesCompat.getDrawable(resources, expectedId, target.context.theme)
                ?: return false
        val bitmap = getBitmap(target.drawable)
        val otherBitmap = getBitmap(expectedDrawable)

        return bitmap.sameAs(otherBitmap)
    }

    private fun getBitmap(drawable: Drawable): Bitmap {
        val bitmap = Bitmap.createBitmap(
            drawable.intrinsicWidth,
            drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
        return bitmap
    }

    override fun describeTo(description: Description?) {
        description?.appendText("isMatchingDrawableResource")
    }
}

有了这个我只问 checkBackground 我想检查的位置和 imageRes 是否存在。我也在检查它是否在更下方可见。我尝试在代码中进一步使用 Espresso 中的 isDisplayed,但不知何故不起作用。