如何解决如何从 Paging 3 测试 PagingData
我的 viewmodel 有一个返回 PagingData 流的方法。在我的应用中,数据是从远程服务器获取的,然后保存到 Room(唯一的真实来源):
fun getChocolates(): Flow<PagingData<Chocolate>> {
val pagingSourceFactory = { dao().getChocolateListData() }
return Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,maxSize = MAX_MEMORY_SIZE,enablePlaceholders = false
),remoteMediator = ChocolateRemoteMediator(
api,dao
),pagingSourceFactory = pagingSourceFactory
).flow
}
我如何测试这个方法?我想测试返回的流是否包含正确的数据。
到目前为止我尝试过的:
@InternalCoroutinesApi
@Test
fun getChocolateListReturnsCorrectData() = runBlockingTest {
val chocolateListDao: ChocolateListDao by inject()
val chocolatesRepository: ChocolatesRepository by inject()
val chocolatelistadapter: Chocolatelistadapter by inject()
// 1
val chocolate1 = Chocolate(
name = "Dove"
)
val chocolate2 = Chocolate(
name = "Hershey's"
)
// 2
// You need to launch here because submitData suspends forever while PagingData is alive
val job = launch {
chocolatesRepository.getChocolateListStream().collectLatest {
chocolatelistadapter.submitData(it)
}
}
// Do some stuff to trigger loads
chocolateListDao.saveChocolate(chocolate1,chocolate2)
// How to read from adapter state,there is also .peek() and .itemCount
assertEquals(listof(chocolate1,chocolate2).toMutableList(),chocolatelistadapter.snapshot())
// We need to cancel the launched job as coroutines.test framework checks for leaky jobs
job.cancel()
}
我想知道我是否在正确的轨道上。任何帮助将不胜感激!
解决方法
基本上有两种方法可以解决此问题,具体取决于您是需要转换前数据还是转换后数据。
如果您只想断言存储库结束,您的查询是正确的 - 您可以直接查询 PagingSource
,但这是预转换,因此您对 ViewModel 中的 PagingData 所做的任何映射或过滤都将获胜不计入这里。但是,如果您想直接测试查询,则更“纯粹”。
@Test
fun repo() = runBlockingTest {
val pagingSource = MyPagingSource()
val loadResult = pagingSource.load(...)
assertEquals(
expected = LoadResult.Page(...),actual = loadResult,)
}
另一方面,如果您关心转换,则需要将数据从 PagingData
加载到演示器 API。
@Test
fun ui() = runBlockingTest {
val viewModel = ... // Some AndroidX Test rules can help you here,but also some people choose to do it manually.
val adapter = MyAdapter(..)
// You need to launch here because submitData suspends forever while PagingData is alive
val job = launch {
viewModel.flow.collectLatest {
adapter.submitData(it)
}
}
... // Do some stuff to trigger loads
advanceUntilIdle() // Let test dispatcher resolve everything
// How to read from adapter state,there is also .peek() and .itemCount
assertEquals(...,adapter.snapshot())
// We need to cancel the launched job as coroutines.test framework checks for leaky jobs
job.cancel()
}
,
我发现使用 Turbine from cashapp 会容易得多。(JakeWharton 又来拯救了:P)
testImplementation "app.cash.turbine:turbine:0.2.1"
根据您的代码,我认为您的测试用例应该如下所示:
@ExperimentalTime
@ExperimentalCoroutinesApi
@Test
fun `test if receive paged chocolate data`() = runBlockingTest {
val expected = listOf(
Chocolate(name = "Dove"),Chocolate(name = "Hershey's")
)
coEvery {
dao().getChocolateListData()
}.returns(
listOf(
Chocolate(name = "Dove"),Chocolate(name = "Hershey's")
)
)
launchTest {
viewModel.getChocolates().test(
timeout = Duration.ZERO,validate = {
val collectedData = expectItem().collectData()
assertEquals(expected,collectedData)
expectComplete()
})
}
}
我还准备了一个基本的 ViewModelTest 类来处理大部分设置和拆卸任务:
abstract class BaseViewModelTest {
@get:Rule
open val instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
open val testCoroutineRule = CoroutineTestRule()
@MockK
protected lateinit var owner: LifecycleOwner
private lateinit var lifecycle: LifecycleRegistry
@Before
open fun setup() {
MockKAnnotations.init(this)
lifecycle = LifecycleRegistry(owner)
every { owner.lifecycle } returns lifecycle
}
@After
fun tearDown() {
clearAllMocks()
}
protected fun initCoroutine(vm: BaseViewModel) {
vm.apply {
setViewModelScope(testCoroutineRule.testCoroutineScope)
setCoroutineContext(testCoroutineRule.testCoroutineDispatcher)
}
}
@ExperimentalCoroutinesApi
protected fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineRule.runBlockingTest(block)
protected fun launchTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineRule.testCoroutineScope.launch(testCoroutineRule.testCoroutineDispatcher) { block }
}
至于从answer from another post借来的扩展函数collectData()
(谢谢@Farid!!)
以及介绍turbine
的幻灯片版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。