我在使用 Kakao 的 UI 测试中操作具有不同 ViewType 的 RecyclerView 时遇到问题:
RecyclerView 用于自动完成器,有两种 ViewType:
我想要实现的目标:单击 RecyclerView 中的第一个项目,该项目具有与我的“常规”项目匹配的视图。注意:RecyclerView 中的第一个项目没有此类型,而是上面提到的“特殊项目”类型。
视图 xml 如下所示:
<androidx.constraintlayout.widget.ConstraintLayout
...
android:id="@+id/auto_completer_item_root" ...>
<TextView
android:id="@+id/destination_title"
...
/>
...
</androidx.constraintlayout.widget.ConstraintLayout>
和
<androidx.constraintlayout.widget.ConstraintLayout
...
android:id="@+id/gps_search_item_root" ...>
<TextView
android:id="@+id/gps_title"
...
/>
...
</androidx.constraintlayout.widget.ConstraintLayout>
我有一个 RecyclerView 的屏幕对象:
class AutoCompleterScreen : KScreen<AutoCompleterScreen>() {
override val layoutId: Int? = null
override val viewClass: Class<*>? = null
val resultList = KRecyclerView(
builder = { withId(R.id.destinationList) },
itemTypeBuilder = {
itemType(::GpsSearch)
itemType(::AutoCompleterResult)
}
)
internal class AutoCompleterResult(parent: Matcher<View>) : KRecyclerItem<AutoCompleterResult>(parent) {
val title: KTextView = KTextView(parent) { withId(R.id.destination_title) }
}
internal class GpsSearch(parent: Matcher<View>) : KRecyclerItem<GpsSearch>(parent) {
val title: KTextView = KTextView(parent) { withId(R.id.gps_title) }
}
}
我在测试中尝试了不同的方法,我希望这会起作用:
onScreen<AutoCompleterScreen> {
resultList {
firstChild<AutoCompleterScreen.AutoCompleterResult> {
title {
click()
}
}
}
}
但事实并非如此。
我不断试验,这个成功了一次,但我无法重复,所以这可能与应用程序没有新安装有关:
onScreen<AutoCompleterScreen> {
resultList {
childWith<AutoCompleterScreen.AutoCompleterResult> {
withId(R.id.auto_completer_item_root)
isFirst()
} perform {
click()
}
}
}
另一方面,我设法想出了一些实际有效的 Espresso 代码 - 但当我使用 Kaspresso 时,我想学习如何利用该框架:
onView(withId(R.id.destinationList))
.perform(actionOnFirstItemWithId(R.id.auto_completer_item_root, click()))
使用
fun actionOnFirstItemWithId(@IdRes viewId: Int, action: ViewAction): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View> {
return Matchers.allOf(ViewMatchers.isAssignableFrom(RecyclerView::class.java), ViewMatchers.isDisplayed())
}
override fun getDescription(): String {
return "Perform action on the first item that has a view with the given ID."
}
override fun perform(uiController: UiController, view: View) {
val recyclerView = view as RecyclerView
for (i in 0 until recyclerView.adapter!!.itemCount) {
uiController.loopMainThreadForAtLeast(500)
val viewHolder = recyclerView.findViewHolderForAdapterPosition(i)
if (viewHolder?.itemView?.findViewById<View>(viewId) != null) {
if (i != 0) {
recyclerView.scrollToPosition(i)
uiController.loopMainThreadForAtLeast(500)
}
val targetView = viewHolder.itemView.findViewById<View>(viewId)
action.perform(uiController, targetView)
return
}
}
}
}
}
也许,Kakao/Kaspresso 在主线程循环方面存在问题,但由于其内部的 flakysafe 实现,它应该支持没有循环的问题。
有人可以帮助我使用 Kakao/Kaspresso 吗?
您能否在失败的运行期间也发送 Kaspresso 的日志?我可能也有类似的问题,但如果没有看到它在哪里失败,我不能肯定地说。
其实就是代码
onScreen<AutoCompleterScreen> {
resultList {
childWith<AutoCompleterScreen.AutoCompleterResult> {
withId(R.id.auto_completer_item_root)
isFirst()
} perform {
click()
}
}
}
有两个缺陷:
a)
auto_completer_item_root
似乎不是 RecyclerView 项目的一部分,我需要将其与 destination_title
交换。最后,我什至可以删除 withId
检查,因为 Kakao 使用 childWith
进行了该检查。
b) 在我的场景中,RecyclerView 预先填充了先前搜索的数据,这意味着符合上述条件的项目已经在 RecyclerView 中,但在 RecyclerView 使用当前运行的更新更新之前,并不打算单击它们搜索(在测试的早期发布)。因此,我需要添加一个检查,以确保搜索结果适合当前关键字。
这是工作代码:
onScreen<AutoCompleterScreen> {
resultList {
childWith<AutoCompleterScreen.AutoCompleterResult> {
containsText("current keyword")
isFirst()
} perform {
click()
}
}
}