当我在视图模型中添加协程delay()时,将不执行代码的其余部分。
这是我的演示代码:
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)
}
}
我得到了断言结果:
java.lang.AssertionError:
Expected :1
Actual :0
有什么解决方法吗?
答案 0 :(得分:2)
您启动一个协程,该协程在将data
设置为1之前会暂停1秒钟。您的测试只会调用doSomething
,而不会等到实际设置data
时才会执行。如果您将另一个更长的delay
添加到测试中,它将可以正常工作:
@Test
fun coroutineDelay() = runBlocking {
...
viewModel.doSomething()
delay(1100)
...
}
您还可以使协程返回Deferred
,您可以等待:
fun doSomething(): Deferred<Unit> {
return async {
delay(1000)
data = 1
}
}
有了await
,就不再需要延迟代码了:
val model = SimpleViewModel()
model.doSomething().await()
答案 1 :(得分:2)
代码中的第一个问题是SimpleViewModel.coroutineContext
没有与其关联的Job
。使视图模型成为CoroutineScope
的全部要点是能够集中取消所有它开始的协程。因此,按如下所示添加作业(请注意自定义获取程序的 absence ):
class SimpleViewModel : ViewModel(), CoroutineScope {
override val coroutineContext = Job() + 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
runBlocking {
viewModel.coroutineContext[Job]!!.children.forEach { it.join() }
}
Assert.assertEquals(1, viewModel.data)
}
}