Espresso "scrollTo(Matcher<...>)" 无法处理数据绑定项

Espresso "scrollTo(Matcher<...>)" not working with data bound items

虽然我可以滚动到我的 RecyclerView 的特定索引,但我无法滚动到满足依赖于数据绑定值的给定匹配器的项目。

如果我使用值对插入到 RecyclerView 中的项目的布局进行硬编码,则 Matcher 会找到它们,但如果这些值是通过数据绑定填充的,则 Matcher 正在比较的值都是“空的”。即使挂起的绑定已全部完成并且没有任何等待(或休眠)或任何东西可以使它们可用,也会发生这种情况。

同样有趣的是,Espresso 的“withText”匹配器可以 'see' 值 运行 作为一个简单的断言,但当在“scrollTo(...) " 找不到它们的方法。

值得注意的是,“scrollTo(...)”方法确实会导致我的适配器的“onCreateViewHolder(...)”再次 运行 但适配器的“onBindViewHolder(...)”在检查开始之前也是 运行 并且在这些方法中插入 IdlingResource 阻止程序没有帮助。

知道这里发生了什么吗?

编辑:我做了一个简单的独立项目来说明这个问题,试图找出问题所在。 添加具体代码作为例子

“text_row_item”的布局文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="text"
            type="String"
            />
    </data>

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{text}"
        />
</layout>

我的适配器是这样的:

class CustomAdapter(private val dataSet: List<String>) :
    RecyclerView.Adapter<CustomAdapter.CustomViewHolder>() {

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): CustomViewHolder {

        val binding: ViewDataBinding = DataBindingUtil.inflate(
            LayoutInflater.from(viewGroup.context),
            R.layout.text_row_item,
            viewGroup,
            false)
        binding.executePendingBindings()
        return CustomViewHolder(binding)
    }

    override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        holder.bind(dataSet[position])
    }

    inner class CustomViewHolder(private val binding: ViewDataBinding)
        : RecyclerView.ViewHolder(binding.root) {
        fun bind(item: String) {
            binding.setVariable(BR.text, item)
        }
    }

    override fun getItemCount() = dataSet.size
}

MainActivity.kt:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(this)

        recyclerView.adapter = CustomAdapter(listOf(
            "W00t",
            "W01t",

            .
            .
            .

            "W41t",
            "W42t"
        ))
    }
}

我实际的 Espresso 测试文件本身包含:

@Test
fun checkW00_42t() {
    withText("W00t").assertAny(isDisplayed())

    checkText("W00t")
    checkText("W01t")

    .
    .
    .

    checkText("W41t")
    checkText("W42t")
}

private fun checkText(text: String) {
    Espresso.onView(withId(R.id.recycler_view))
        .perform(RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
            hasDescendant(withText(text))))

    withText(text).assertAny(isDisplayed())

这就是这个应用程序在测试时的样子 运行:

RecyclerView 添加了 43 个“W00t”,因此需要滚动它才能看到最后一个,但它甚至找不到第一个根本不需要滚动的。

值得注意的是第一次检查(不使用“scrollTo(...)”)通过了:

withText("W00t").assertAny(isDisplayed())

但是尝试滚动到完全相同的文本失败:

checkText("W00t")

还值得注意的是,如果不使用 DataBinding 而只是在 onBindViewHolder 中分配文本,如:

viewHolder.textView.text = dataSet[position]

scrollTo(...) 有效。

我的天啊,我想我得到了!

我不得不将 executePendingBindings()onCreateViewHolder(...) 移动到 CustomViewHolder():

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): CustomViewHolder {
     val binding: ViewDataBinding = DataBindingUtil.inflate(...)
     // binding.executePendingBindings() // ** remove here **
     return CustomViewHolder(binding)
}

inner class CustomViewHolder(private val binding: ViewDataBinding)
    : RecyclerView.ViewHolder(binding.root) {
    fun bind(item: String) {
        binding.setVariable(BR.text, item)
        binding.executePendingBindings() // ** insert here **
    }
}