1

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

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

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

关于我们如何监听回收器视图本身的更改的任何想法?

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

4

1 回答 1

0

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.

于 2020-07-16T10:39:15.253 回答