[英]Test LiveData and Coroutines using MockK
I have this view model:我有这个视图模型:
class MyViewModel(private val myUseCase: MyUseCase) : ViewModel() {
val stateLiveData = MutableLiveData(State.IDLE)
fun onButtonPressed() {
viewModelScope.launch {
stateLiveData.value = State.LOADING
myUseCase.loadStuff() // Suspend
stateLiveData.value = State.SUCCESS
}
}
}
I'd like to write a test that checks whether the state is really LOADING
while myUseCase.loadStuff()
is running.我想编写一个测试,检查在
myUseCase.loadStuff()
运行时状态是否真的为LOADING
。 I'm using MockK for that.我正在使用 MockK。 Here's the test class:
这是测试类:
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var myUseCase: MyUseCase
private lateinit var myViewModel: MyViewModel
@Before
fun setup() {
myUseCase = mockkClass(MyUseCase::class)
myViewModel = MyViewModel(myUseCase)
}
@Test
fun `button click should put screen into loading state`() = runBlockingTest {
coEvery { myUseCase.loadStuff() } coAnswers { delay(2000) }
myViewModel.onButtonPressed()
advanceTimeBy(1000)
val state = myViewModel.stateLiveData.value
assertEquals(State.LOADING, state)
}
}
It fails:它失败:
java.lang.AssertionError:
Expected :LOADING
Actual :IDLE
How can I fix this?我怎样才能解决这个问题?
I only needed to make a few changes in the test class to make it pass:我只需要在测试类中进行一些更改即可通过:
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
private val dispatcher = TestCoroutineDispatcher()
private lateinit var myUseCase: MyUseCase
private lateinit var myViewModel: MyViewModel
@Before
fun setup() {
Dispatchers.setMain(dispatcher)
myUseCase = mockkClass(MyUseCase::class)
myViewModel = MyViewModel(myUseCase)
}
@After
fun cleanup() {
Dispatchers.resetMain()
}
@Test
fun `button click should put screen into loading state`() {
dispatcher.runBlockingTest {
coEvery { myUseCase.loadStuff() } coAnswers { delay(2000) }
myViewModel.onButtonPressed()
// This isn't even needed.
advanceTimeBy(1000)
val state = myViewModel.stateLiveData.value
assertEquals(State.LOADING, state)
}
}
}
No changes needed in the view model at all!视图模型根本不需要更改! :D
:D
Thanks Kiskae for such helpful advice!感谢 Kiskae 提供如此有用的建议!
Your problem lies in the fact that viewModelScope
dispatches to Dispatcher.MAIN
, not the testing dispatcher created by runBlockingTest
.您的问题在于
viewModelScope
分派到Dispatcher.MAIN
,而不是由runBlockingTest
创建的测试分派runBlockingTest
。 This means that even with the call to advanceTimeBy
the code does not get executed.这意味着即使调用了
advanceTimeBy
代码也不会被执行。
You can solve the issue by using Dispatcher.setMain(..)
to replace the MAIN dispatcher with your test dispatcher.您可以通过使用
Dispatcher.setMain(..)
将 MAIN 调度程序替换为您的测试调度程序来解决该问题。 This will require managing the dispatcher yourself instead of relying on the stand-alone runBlockingTest
.这将需要您自己管理调度程序,而不是依赖于独立的
runBlockingTest
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.