当协程包含协程延迟时,如何对其进行单元测试?

时间:2018-11-12 23:34:24

标签: android unit-testing kotlin coroutine kotlin-coroutines

当我在视图模型中添加协程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

有什么解决方法吗?

2 个答案:

答案 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)
    }
}