如何从分页中测试PagingData



MyViewModel有一个返回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()
}

我想知道我是否在正确的轨道上。任何帮助将非常感激!

我发现使用来自cashapp的涡轮机会容易得多。(杰克·沃顿再次前来救援: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类来处理许多设置和tearDown任务:

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 }
}

至于扩展函数collectData(),这是从另一个帖子的答案借来的(谢谢@Farid!!)

和介绍涡轮机的幻灯片

基本上有两种方法,这取决于你是想要转换前的数据还是转换后的数据。

如果你想只是断言存储库结束,你的查询是正确的-你可以直接查询PagingSource,这是预转换,虽然这样你做的任何映射或过滤你做的PagingData在ViewModel不会在这里被解释。然而,它更"纯粹"。如果您想直接测试查询。

@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()
}

最新更新