首页 > 解决方案 > 在 Android 单元测试中检查 PagingData 对象数据的正确方法是什么

问题描述

我正在使用分页库从 api 检索数据并将它们显示在列表中

为此,我在我的存储库中创建了该方法:

fun getArticleList(query: String): Flow<PagingData<ArticleHeaderData>>

在我的视图模型中,我创建了类似这样的搜索方法:

override fun search(query: String) {
    val lastResult = articleFlow
    if (query == lastQuery && lastResult != null)
        return
    lastQuery = query
    searchJob?.cancel()
    searchJob = launch {
        val newResult: Flow<PagingData<ArticleList>> = repo.getArticleList(query)
            .map {
                it.insertSeparators { //code to add separators }.cachedIn(this)
        articleFlow = newResult
        newResult.collectLatest {
            articleList.postValue(it)
        }
    }
}

为了测试我的视图模型,我正在使用测试方法PagingData.from创建一个流,以便从我的模拟存储库返回,如下所示:

whenever(repo.getArticleList(query)).thenReturn(flowOf(PagingData.from(articles)))

然后我从 articleList LiveData 中检索实际的分页数据,如下所示:

val data = vm.articleList.value!!

这将返回一个PagingData<ArticleList>对象,我想验证它是否包含来自服务的数据(即articles无论何时返回)

我发现这样做的唯一方法是创建以下扩展函数:

private val dcb = object : DifferCallback {
    override fun onChanged(position: Int, count: Int) {}
    override fun onInserted(position: Int, count: Int) {}
    override fun onRemoved(position: Int, count: Int) {}
}

suspend fun <T : Any> PagingData<T>.collectData(): List<T> {
    val items = mutableListOf<T>()
    val dif = object : PagingDataDiffer<T>(dcb, TestDispatchers.Immediate) {
        override suspend fun presentNewList(previousList: NullPaddedList<T>, newList: NullPaddedList<T>, newCombinedLoadStates: CombinedLoadStates, lastAccessedIndex: Int): Int? {
            for (idx in 0 until newList.size)
                items.add(newList.getFromStorage(idx))
            return null
        }
    }
    dif.collectFrom(this)
    return items
}

这似乎有效,但基于PagingDataDiffer标记为的类,@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)因此将来可能无法使用

有没有更好的方法可以flow从 PagingData (在库中标记为内部)或从中获取实际数据?

标签: androidunit-testingandroid-paging

解决方案


我对 Paging3 库也有同样的问题,网上还没有很多关于这个库的讨论,但是当我浏览一些文档时,我可能会找到一个解决方案。我面临的场景是试图确定一个数据列表是否为空PagingData,然后我将在此基础上操作 UI。

这是我在文档中找到的内容,在3.0.0-alpha04PagingDataAdapter版本中添加了两个 api,即, 和,为我们提供了基于索引的特定列表对象,而为我们提供了整个列表。peek()snapshot()peek()snapshot()

所以这就是我所做的:

lifecycleScope.launch {
    //Your PagingData flow submits the data to your RecyclerView adapter
    viewModel.allConversations.collectLatest {
        adapter.submitData(it)
    }
}
lifecycleScope.launch {
    //Your adapter's loadStateFlow here
    adapter.loadStateFlow.
        distinctUntilChangedBy {
            it.refresh
        }.collect {
            //you get all the data here
            val list = adapter.snapshot()
            ...
        }
    }

由于我刚刚接触到 Paging 库,Flow最近,这种方法可能存在缺陷,如果有更好的方法,请告诉我!


推荐阅读