问题
When I add a coroutine delay() in my view model, the remaining part of the code will not be executed.
This is my demo code:
class SimpleViewModel : ViewModel(), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.Unconfined
var data = 0
fun doSomething() {
launch {
delay(1000)
data = 1
}
}
}
class ScopedViewModelTest {
@Test
fun coroutineDelay() {
// Arrange
val viewModel = SimpleViewModel()
// ActTes
viewModel.doSomething()
// Assert
Assert.assertEquals(1, viewModel.data)
}
}
I got the assertion result:
java.lang.AssertionError:
Expected :1
Actual :0
Any idea how to fix this?
回答1:
You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:
@Test
fun coroutineDelay() = runBlocking {
...
viewModel.doSomething()
delay(1100)
...
}
You can also make the coroutine return a Deferred which you can wait on:
fun doSomething(): Deferred<Unit> {
return async {
delay(1000)
data = 1
}
}
With await there's no need to delay your code anymore:
val model = SimpleViewModel()
model.doSomething().await()
回答2:
The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):
class SimpleViewModel : ViewModel(), CoroutineScope {
override val coroutineContext = Job() + Dispatchers.Unconfined
var data = 0
fun doSomething() {
launch {
delay(1000)
data = 1
}
}
}
Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:
class ScopedViewModelTest {
@Test
fun coroutineDelay() {
// Arrange
val viewModel = SimpleViewModel()
// ActTes
viewModel.doSomething()
// Assert
runBlocking {
viewModel.coroutineContext[Job]!!.children.forEach { it.join() }
}
Assert.assertEquals(1, viewModel.data)
}
}
来源:https://stackoverflow.com/questions/53271646/how-to-unit-test-coroutine-when-it-contains-coroutine-delay