【问题标题】:DiffUtil espresso TestDiffUtil 浓缩咖啡测试
【发布时间】:2020-07-15 00:18:46
【问题描述】:

[测试] 我们如何为 DiffUtil 编写测试用例?

val oldList = listOf("a", "b", 0)
val newList = listOf("a", "c", 0, 1)

我们如何编写一个特定的测试用例,我们的onBindViewHolder 没有被调用新列表中相对于旧列表未修改的项目,这基本上是DiffUtil 所做的以使一切顺利?

知道如何监听回收器视图本身的变化吗?

我正在考虑使用registerAdapterDataObserver(AdapterDataObserver),我们可以覆盖onItemRangeChanged 并听取更改,但在测试中使用这种方法并不好。

【问题讨论】:

    标签: android unit-testing android-recyclerview android-espresso android-diffutils


    【解决方案1】:

    DiffUtil 不会再次重新绑定任何相同的项目,因此根据要求,如果此测试用例使用 DiffUtil 以外的任何其他实现运行,它将失败。

      @Test
      fun testAdapter_noRebinding() {
    
        val adapter = getAdapterObject()
    
        lateinit var dataObserver: RecyclerView.AdapterDataObserver
        var doesRebind = false
    
        val oldList = listOf("a", "b", 0)
        val newList = listOf("a", "b", 0)
    
        ActivityScenario.launch(AdapterActivity::class.java).use { scenario ->
    
          scenario.onActivity { activity ->
            dataObserver = object: RecyclerView.AdapterDataObserver(){
              override fun onChanged() {
                doesRebind = true
              }
    
              override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
                assertThat(positionStart).isEqualTo(0)
                assertThat(itemCount).isEqualTo(3)
              }
    
              override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
                doesRebind = true
              }
    
              override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
                doesRebind = true
              }
            }
            adapter.registerAdapterDataObserver(dataObserver)
          }
    
          scenario.onActivity { activity ->
            val liveData = getList(activity)
            liveData.value = oldList
          }
    
          scenario.onActivity { activity ->
            val liveData = getList(activity)
            liveData.value = newList
          }
    
          scenario.onActivity { activity ->
            assertThat(doesRebind).isEqualTo(false)
            adapter.unregisterAdapterDataObserver(dataObserver)
          }
        }
      }
    

    更新

    使用 Mockito 添加了测试用例,但这些测试用例仅通过 robolectric 而不是 espresso

     @Test
      fun testBindableAdapter_incomingSameData_noRebindingShouldHappen() {
        val adapter = createMultiViewTypeNoDataBindingBindableAdapter()
        BindableAdapterTestFragmentPresenter.testBindableAdapter = adapter
    
        val oldList = listOf(STR_VALUE_1, STR_VALUE_0, INT_VALUE_1).toMutableList()
        val newList = listOf(STR_VALUE_1, STR_VALUE_0, INT_VALUE_1).toMutableList()
    
        val fakeObserver = mock(RecyclerView.AdapterDataObserver::class.java)
    
        ActivityScenario.launch(BindableAdapterTestActivity::class.java).use { scenario ->
          scenario.onActivity { activity ->
            adapter.registerAdapterDataObserver(fakeObserver)
            val liveData = getRecyclerViewListLiveData(activity)
            liveData.value = oldList
            verify(fakeObserver, times(1)).onItemRangeInserted(0, 3)
            val liveDataNew = getRecyclerViewListLiveData(activity)
            liveDataNew.value = newList
            verify(fakeObserver, never()).onChanged()
            adapter.unregisterAdapterDataObserver(fakeObserver)
          }
        }
      }
    
      @Test
      fun testBindableAdapter_removeOneItem_verifyChangeOnlyOneItem() {
        val adapter = createMultiViewTypeNoDataBindingBindableAdapter()
        BindableAdapterTestFragmentPresenter.testBindableAdapter = adapter
    
        val oldList = listOf(STR_VALUE_1, STR_VALUE_1, INT_VALUE_1).toMutableList()
        val newList = listOf(STR_VALUE_1, INT_VALUE_1).toMutableList()
    
        val fakeObserver = mock(RecyclerView.AdapterDataObserver::class.java)
    
        ActivityScenario.launch(BindableAdapterTestActivity::class.java).use { scenario ->
          scenario.onActivity { activity ->
            adapter.registerAdapterDataObserver(fakeObserver)
            val liveData = getRecyclerViewListLiveData(activity)
            liveData.value = oldList
            val liveDataNew = getRecyclerViewListLiveData(activity)
            liveDataNew.value = newList
            verify(fakeObserver, times(1)).onItemRangeRemoved(0, 1)
            adapter.unregisterAdapterDataObserver(fakeObserver)
          }
        }
      }
    
      @Test
      fun testBindableAdapter_insertOneItem_verifyChangeOnlyOneItem() {
        val adapter = createMultiViewTypeNoDataBindingBindableAdapter()
        BindableAdapterTestFragmentPresenter.testBindableAdapter = adapter
    
        val oldList = listOf(STR_VALUE_1, INT_VALUE_1).toMutableList()
        val newList = listOf(STR_VALUE_1, STR_VALUE_1, INT_VALUE_1).toMutableList()
    
        val fakeObserver = mock(RecyclerView.AdapterDataObserver::class.java)
    
        ActivityScenario.launch(BindableAdapterTestActivity::class.java).use { scenario ->
          scenario.onActivity { activity ->
            adapter.registerAdapterDataObserver(fakeObserver)
            val liveData = getRecyclerViewListLiveData(activity)
            liveData.value = oldList
            val liveDataNew = getRecyclerViewListLiveData(activity)
            liveDataNew.value = newList
            verify(fakeObserver, times(1)).onItemRangeInserted(0, 1)
            adapter.unregisterAdapterDataObserver(fakeObserver)
          }
        }
      }
    
      @Test
      fun testBindableAdapter_moveOneItem_verifyNoRecreatingWholeList() {
        val adapter = createMultiViewTypeNoDataBindingBindableAdapter()
        BindableAdapterTestFragmentPresenter.testBindableAdapter = adapter
    
        val oldList = listOf(STR_VALUE_1, STR_VALUE_0, INT_VALUE_1).toMutableList()
        val newList = listOf(INT_VALUE_1, STR_VALUE_0, STR_VALUE_1).toMutableList()
    
        val fakeObserver = mock(RecyclerView.AdapterDataObserver::class.java)
    
        ActivityScenario.launch(BindableAdapterTestActivity::class.java).use { scenario ->
          scenario.onActivity { activity ->
            adapter.registerAdapterDataObserver(fakeObserver)
            val liveData = getRecyclerViewListLiveData(activity)
            liveData.value = oldList
            val liveDataNew = getRecyclerViewListLiveData(activity)
            liveDataNew.value = newList
            verify(fakeObserver, times(1)).onItemRangeChanged(2, 1, null)
            verify(fakeObserver, times(1)).onItemRangeChanged(0, 1, null)
            adapter.unregisterAdapterDataObserver(fakeObserver)
          }
        }
      }
    
      @Test
      fun testBindableAdapter_updateOneItemContent_verifyOneItemUpdated() {
        val adapter = createMultiViewTypeNoDataBindingBindableAdapter()
        BindableAdapterTestFragmentPresenter.testBindableAdapter = adapter
    
        val oldList = listOf(STR_VALUE_1, STR_VALUE_0, INT_VALUE_1).toMutableList()
        val newList = listOf(STR_VALUE_1, STR_VALUE_1, INT_VALUE_1).toMutableList()
    
        val fakeObserver = mock(RecyclerView.AdapterDataObserver::class.java)
    
        ActivityScenario.launch(BindableAdapterTestActivity::class.java).use { scenario ->
          scenario.onActivity { activity ->
    
            adapter.registerAdapterDataObserver(fakeObserver)
            val liveData = getRecyclerViewListLiveData(activity)
            liveData.value = oldList
            val liveDataNew = getRecyclerViewListLiveData(activity)
            liveDataNew.value = newList
            verify(fakeObserver, times(1)).onItemRangeChanged(1, 1, null)
            adapter.unregisterAdapterDataObserver(fakeObserver)
          }
        }
      }
    

    浓缩咖啡出错

    java.lang.RuntimeException: Wanted but not invoked:
    adapterDataObserver.onItemRangeRemoved(0, 1);
    -> at org.oppia.app.recyclerview.BindableAdapterTest$testBindableAdapter_removeOneItem_verifyChangeOnlyOneItem$$inlined$use$lambda$1.perform(BindableAdapterTest.kt:303)
    Actually, there were zero interactions with this mock.
    

    java.lang.RuntimeException: Wanted but not invoked:
    adapterDataObserver.onItemRangeChanged(
    2,
    1,
    null
    );
    -> at org.oppia.app.recyclerview.BindableAdapterTest$testBindableAdapter_moveOneItem_verifyNoRecreatingWholeList$$inlined$use$lambda$1.perform(BindableAdapterTest.kt:349)
    Actually, there were zero interactions with this mock.
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多