【问题标题】:How to unit test coroutine when it contains coroutine delay?当协程包含协程延迟时如何对协程进行单元测试?
【发布时间】:2019-04-15 17:36:42
【问题描述】:

当我在我的视图模型中添加协程延迟()时,其余部分代码将不会执行。

这是我的演示代码:

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

知道如何解决这个问题吗?

【问题讨论】:

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


    【解决方案1】:

    您的代码中的第一个问题是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)
        }
    }
    

    【讨论】:

      【解决方案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()
      

      【讨论】:

      • 有没有办法在测试中不包括具体的延迟数?
      • 以异步为例
      猜你喜欢
      • 2018-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-07
      • 2019-12-17
      • 2018-05-13
      • 2021-08-04
      • 1970-01-01
      相关资源
      最近更新 更多