包含协程延迟时如何对协程进行单元测试?
How to unit test coroutine when it contains coroutine delay?
当我在我的视图模型中添加协程 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
知道如何解决这个问题吗?
您启动了一个协程,该协程在将 data
设置为 1 之前暂停 1 秒。您的测试仅调用 doSomething
但不会等到 data
实际被设置。如果您添加另一个更长的 delay
到测试中,它将起作用:
@Test
fun coroutineDelay() = runBlocking {
...
viewModel.doSomething()
delay(1100)
...
}
你也可以让协程return成为你可以等待的Deferred
:
fun doSomething(): Deferred<Unit> {
return async {
delay(1000)
data = 1
}
}
有了 await
就没有必要再延迟你的代码了:
val model = SimpleViewModel()
model.doSomething().await()
您的代码中的第一个问题是 SimpleViewModel.coroutineContext
没有与之关联的 Job
。使您的视图模型成为 CoroutineScope
的全部意义在于能够集中取消它启动的所有协程。所以添加作业如下(注意自定义getter的不存在):
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)
}
}
当我在我的视图模型中添加协程 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
知道如何解决这个问题吗?
您启动了一个协程,该协程在将 data
设置为 1 之前暂停 1 秒。您的测试仅调用 doSomething
但不会等到 data
实际被设置。如果您添加另一个更长的 delay
到测试中,它将起作用:
@Test
fun coroutineDelay() = runBlocking {
...
viewModel.doSomething()
delay(1100)
...
}
你也可以让协程return成为你可以等待的Deferred
:
fun doSomething(): Deferred<Unit> {
return async {
delay(1000)
data = 1
}
}
有了 await
就没有必要再延迟你的代码了:
val model = SimpleViewModel()
model.doSomething().await()
您的代码中的第一个问题是 SimpleViewModel.coroutineContext
没有与之关联的 Job
。使您的视图模型成为 CoroutineScope
的全部意义在于能够集中取消它启动的所有协程。所以添加作业如下(注意自定义getter的不存在):
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)
}
}