【发布时间】:2020-09-18 11:41:29
【问题描述】:
最近是 introduced 类 StateFlow 作为 Kotlin 协程的一部分。
我目前正在尝试它并在尝试对我的 ViewModel 进行单元测试时遇到问题。我想要实现的目标:测试我的StateFlow 在我的 ViewModel 中以正确的顺序接收所有状态值。
我的代码如下:
视图模型:
class WalletViewModel(private val getUserWallets: GetUersWallets) : ViewModel() {
val userWallet: StateFlow<State<UserWallets>> get() = _userWallets
private val _userWallets: MutableStateFlow<State<UserWallets>> =
MutableStateFlow(State.Init)
fun getUserWallets() {
viewModelScope.launch {
getUserWallets.getUserWallets()
.onStart { _userWallets.value = State.Loading }
.collect { _userWallets.value = it }
}
}
我的测试:
@Test
fun `observe user wallets ok`() = runBlockingTest {
Mockito.`when`(api.getAssetWallets()).thenReturn(TestUtils.getAssetsWalletResponseOk())
Mockito.`when`(api.getFiatWallets()).thenReturn(TestUtils.getFiatWalletResponseOk())
viewModel.getUserWallets()
val res = arrayListOf<State<UserWallets>>()
viewModel.userWallet.toList(res) //doesn't works
Assertions.assertThat(viewModel.userWallet.value is State.Success).isTrue() //works, last value enmited
}
访问发出的最后一个值有效。但我要测试的是所有发出的值都以正确的顺序发出。
使用这段代码:viewModel.userWallet.toList(res) //doesn't works
我收到以下错误:
java.lang.IllegalStateException: This job has not completed yet
at kotlinx.coroutines.JobSupport.getCompletionExceptionOrNull(JobSupport.kt:1189)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest(TestBuilders.kt:53)
at kotlinx.coroutines.test.TestBuildersKt.runBlockingTest$default(TestBuilders.kt:45)
at WalletViewModelTest.observe user wallets ok(WalletViewModelTest.kt:52)
....
我想我遗漏了一些明显的东西。但不知道为什么,因为我刚刚开始使用 Coroutine 和 Flow,并且在不使用我已经使用的 runBlockingTest 时似乎会发生此错误。
编辑: 作为临时解决方案,我将其作为实时数据进行测试:
@Captor
lateinit var captor: ArgumentCaptor<State<UserWallets>>
@Mock
lateinit var walletsObserver: Observer<State<UserWallets>>
@Test
fun `observe user wallets ok`() = runBlockingTest {
viewModel.userWallet.asLiveData().observeForever(walletsObserver)
viewModel.getUserWallets()
captor.run {
Mockito.verify(walletsObserver, Mockito.times(3)).onChanged(capture())
Assertions.assertThat(allValues[0] is State.Init).isTrue()
Assertions.assertThat(allValues[1] is State.Loading).isTrue()
Assertions.assertThat(allValues[2] is State.Success).isTrue()
}
}
【问题讨论】:
-
在调用
viewModel.getUserWallets()之前,你放置这个断言:Assertions.assertThat(viewModel.userWallet.value is State.Init).isTrue()并跳过这部分:val res = arrayListOf<State<UserWallets>>() viewModel.userWallet.toList(res) -
但我也在努力正确测试 StateFlow,我尝试使用
collectIndexed但我遇到了同样的错误。也许是因为这个流程不会在runBlockingTest块的末尾停止,我没有找到任何解决方案如何取消 StateFlow 以完成工作。我想测试它的正确方法是始终仅在发出一些更改后才测试该值。确保在 Dispatchers.Main 上发出这些 -
@executioner 我用临时解决方案更新了我的问题。哪个适用于现在
-
嘿,我找到了一个解决方案:viewModel.userWallet.take(NUMBER_OF_EXPECTED_VALUES).collect { list.add(it) } 然后你可以测试列表值,就像 liveData 解决方案一样。
-
@executioner 太棒了!仍然有我得到“这个工作还没有完成”你在用 runBlockingTest 吗?
标签: android kotlin kotlin-coroutines android-viewmodel kotlin-flow