Android Espresso:单击RecyclerView项内的特定视图

时间:2019-06-20 12:12:48

标签: android android-espresso android-testing

我有以下RecyclerView列表:

enter image description here

我只希望我的测试单击右侧的复选框,而不是整个项目。 这是我尝试过的:

onView(withId(R.id.recycler_view))
    .perform(actionOnViewHolder<ViewHolder>(matcher = { vh ->
        if (vh == null) return@actionOnViewHolder false

        if (vh.position == no) {
            // v1
            (vh.itemView as ViewGroup).findViewById<View>(R.id.checkbox)?.performClick()
            // v2
            (vh.itemView as ViewGroup).getChildAt(2)?.performClick()
        }
        return@actionOnViewHolder false
    }))

以及一些辅助方法/类:

inline fun <reified VH : RecyclerView.ViewHolder> actionOnViewHolder(
    noinline matcher: (VH?) -> Boolean): RecyclerViewActions.PositionableRecyclerViewAction {
    return RecyclerViewActions.actionOnHolderItem(
        RecyclerViewViewHolderMatcher(VH::class.java, matcher), ViewActions.click())
}

class RecyclerViewViewHolderMatcher<VH : RecyclerView.ViewHolder>(
    clazz: Class<VH>,
    private val matcher: (VH?) -> Boolean) : BoundedMatcher<RecyclerView.ViewHolder, VH>(clazz) {

    override fun describeTo(description: Description?) { }

    override fun matchesSafely(item: VH): Boolean = matcher(item)
}

V1:(vh.itemView as ViewGroup).findViewById<View>(R.id.checkbox)返回null,尽管在调试器中我可以看到3个孩子的视图,最后一个孩子的ID为:checkbox

V2:(vh.itemView as ViewGroup).getChildAt(2)返回视图,但是稍后会发生单击。

此外,我总是从false返回actionOnViewHolder,因为我不想click()或整个ViewHolder上的其他任何东西。

有什么办法可以更好地做到这一点?

1 个答案:

答案 0 :(得分:0)

使用辅助方法添加新类:

object EspressoMatchers { 
    fun atPositionOnView(mRecyclerViewId: Int, position: Int, targetViewId: Int): Matcher<View> {

    return object : TypeSafeMatcher<View>() {
        var resources: Resources? = null
        var childView: View? = null

        override fun describeTo(description: Description) {
            val id = targetViewId
            var idDescription = Integer.toString(id)
            if (this.resources != null) {
                try {
                    idDescription = this.resources!!.getResourceName(id)
                } catch (var4: Resources.NotFoundException) {
                    idDescription = String.format("%s (resource name not found)", id)
                }
            }
            description.appendText("with id: $idDescription")
        }

        public override fun matchesSafely(view: View): Boolean {
            this.resources = view.resources
            if (childView == null) {
                val recyclerView: RecyclerView = if (view.rootView.findViewById<View>(mRecyclerViewId) is RecyclerView) {
                    view.rootView.findViewById<View>(mRecyclerViewId) as RecyclerView
                } else {
                    //todo need only if you want acces to recyclerView in custom views
                }
                childView = recyclerView.findViewHolderForAdapterPosition(position)!!.itemView
            }
            val targetView = childView!!.findViewById<View>(targetViewId)
            return view === targetView
        }
    }
}}

创建单元格包装器

open class BaseCell(val listId: Int, val position: Int) {

public fun tap(id: Int) {
    onView(atPositionOnView(listId, position, id)).perform(click())
}
}

然后我们可以在测试中使用它

val cell = BaseCell(R.id.recyclerViewId, 0)
cell.tap(R.id.checkBoxId)