如何对 Returns 来自 Paging 3 的 PagingSource 的 Room Dao 查询进行单元测试

How to Unit Test a Room Dao Query that Returns a PagingSource From Paging 3

我的问题其实很笼统。我想知道如何对 Room Dao 查询 returns a PagingSource 进行单元测试来自 第 3 页.

我有一个 Room Dao 查询:

    @Query("SELECT * FROM database")
    fun getChocolateListData(): PagingSource<Int, Chocolate>

我想知道如何对该查询进行单元测试。

到目前为止我尝试了什么(使用内存 Room 数据库进行测试):

@FlowPreview
@Test
fun saveChocolateToDbSavesData() = runBlocking {
    val dao: Dao by inject()

    val chocolate = Chocolate(
        name = "Dove"
    )
    dao.saveChocolate(chocolate) 

    val pagingSourceFactory = { dao.getChocolateListData() }
    val pagingDataFlow: Flow<PagingData<Chocolate>> = Pager(
        config = PagingConfig(
            pageSize = 50,
            maxSize = 200,
            enablePlaceholders = false
        ),
        pagingSourceFactory = pagingSourceFactory
    ).flow

    val chocolateListFlow = pagingDataFlow.testIn(coroutinesTestRule)
    Assert.assertEquals(PagingData.from(listOf(chocolate)), chocolateListFlow.emissions[0])
}

然而这并没有通过:

junit.framework.AssertionFailedError: Expected :androidx.paging.PagingData@7d6c23a1 Actual :androidx.paging.PagingData@321123d2

不确定如何正确处理。任何帮助将不胜感激!

PagingData 是内部事件流的包装器,您不能直接比较它,并且您得到的错误是按预期抛出引用不平等。

相反,您应该直接查询 PagingSource 以比较 LoadResult.Page 中的数据,或者您需要将其连接到演示者 API,例如 AsyncPagingDataDifferPagingDataAdapter 并使用 .snapshot()

val flow = Pager(..).flow
val adapter = MyPagingDataAdapter()
val job = launch {
    flow.collectLatest { adapter.submitData(it) }
}
// Do your asserts here
job.cancel()

如果您需要测试范围,我推荐 kotlinx.coroutines.test 库中的 runBlockingTest

要直接查询PagingSource,它有一个挂起的.load()方法,所以你可以简单地将它包装在runBlockingTest中并断言结果:

@Test
fun test() = runBlockingTest {
  val pagingSource = MyPagingSource()
  val actual = pagingSource.load(LoadParams.Refresh(...))
  assertEquals(actual as? LoadResult.Page)?.data, listOf(...))
}

以防万一你需要模拟 PagingSource:

创建助手 class PagingSourceUtils.kt 示例:

class PagingSourceUtils<T : Any>(
    private val data: List<T>
) : PagingSource<Int, T>() {
    override fun getRefreshKey(state: PagingState<Int, T>): Int? {
        return 0
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, T> {
        return LoadResult.Page(
            data = data,
            prevKey = null,
            nextKey = null
        )
    }
}

YourTest.kt

@Test
    fun `should success get Chocolate `() {

         val chocolates = listOf(Chocolate(
             name = "Dove"
         )) 

        runBlocking {
            val tData = PagingSourceUtils(chocolates)
            `when`(dao.getChocolateListData()).thenReturn(tData)

            val data = ...

            val actual = ..

            assertEquals(actual, data)
        }
    }

根据标记为正确的答案,我自己做了一个,虽然不是很好,但至少完成了工作,如果有任何反馈我会很高兴,提前致谢。

fun <PaginationKey: Any, Model: Any>PagingSource<PaginationKey, Model>.getData(): List<Model> {
    val data = mutableListOf<Model>()
    val latch = CountDownLatch(1)
    val job = CoroutineScope(Dispatchers.Main).launch {
        val loadResult: PagingSource.LoadResult<PaginationKey, Model> = this@getData.load(
            PagingSource.LoadParams.Refresh(
                key = null, loadSize = Int.MAX_VALUE, placeholdersEnabled = false
            )
        )
        when (loadResult) {
            is PagingSource.LoadResult.Error -> throw loadResult.throwable
            is PagingSource.LoadResult.Page -> data.addAll(loadResult.data)
        }
        latch.countDown()
    }
    latch.await()
    job.cancel()
    return data
}

所以在你的测试中,你可以像这样使用它

val obtainedData = myDao.getSomePagingSource().getData()

assertEquals(expectedData, obtainedData)

警告:您将看到相当长的日志

WARNING: pageSize on the LegacyPagingSource is not set.
When using legacy DataSource / DataSourceFactory with Paging3, page size...