【问题标题】:Testing coroutines in the presenter class在演示者类中测试协程
【发布时间】:2019-07-05 14:46:30
【问题描述】:

我正在努力测试我的演示者,它正在从存储库层调用暂停的函数,如下所示:

 override fun viewCreated() {
        launch {
            val hasPermission = permissionChecker.execute() //suspended function
            if (hasPermission) {
                foo()
            } else {
                view.bar()
            }
         }

presenter也在扩展这个接口:

interface CoroutinePresenter: CoroutineScope {

val job: Job

override val coroutineContext: CoroutineContext
    get() = Dispatchers.Main + job

fun stopAllActiveJobs() {
    coroutineContext.cancelChildren()
}

而挂起函数定义如下:

 suspend fun execute() : Boolean = withContext(Dispatchers.IO) {
    return@withContext class.foo()
}

应用程序中的一切都按预期工作,但是当我尝试编写一些单元测试时,我注意到每当我在 launch 中调用这段代码时,线程都会切换,但测试不会等待执行。这是测试的实现:

@Test
fun `Test of Suspended Function`() = runBlocking {
    presenter.viewCreated()
    then(view).should().bar()
    ...
}

我还添加了用于测试kotlinx-coroutines-test 的建议库,但结果仍然与它相同。我还尝试遵循this 的建议并实施类似this 的方法,但仍然没有运气。 我认为问题是在演示者中调用 launch 时实际创建另一个线程并且测试实际上并不知道如何等待它。我还尝试返回一个 Job 并调用 job.join(),但它以 NullPointerException 失败。

希望你们能帮助我。

【问题讨论】:

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


    【解决方案1】:

    我找到了解决方案: 按照this 教程,我都设置好了

    @Before
    fun setup() {
        Dispatchers.setMain(Dispatchers.Unconfined)
        ...
    }
    @After
    fun tearDown() {
        Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
    }
    

    并且通过在测试中的runBlocking 语句中运行presenter 类的整个launch 块。该问题还与挂起函数中未报告的异常有关,该异常实际上未被模拟,但我看不到它。

    现在一切正常。

    【讨论】:

      【解决方案2】:

      首先,我强烈建议您将 coroutineContext 作为这样的参数:

        class CoroutinePresenter(coroutineContext: CoroutineContext): CoroutineScope {
               init{
                     _coroutineContext = coroutineContext
                }
      
               override val coroutineContext: CoroutineContext
                      get() = _coroutineContext
      
               // Your Methods
      
          }
      

      在您的真实环境中:

       @YourScope
          @Provides
          fun providesCoroutinePresenter(coroutineContext:CoroutineContext ){
              return CoroutinePresenter()
          }
          @YourScope
          @Provides
          fun providesCoroutineContext(){
              return  Dispatchers.Main + job
          }
      

      在单元测试期间:

          @Before
          fun setUp() {
             coroutinePresenter  CoroutinePresenter(Dispatchers.Unconfined)
          }
      
          @Test
          fun `Should do something`(){
               //WHEN
               coroutinePresenter.doSomething(params)
               //THEN
               do your assertions
          }
      

      有关更多信息,请查看 SOLID 原则,对于这种情况D

      【讨论】:

      • 这是一个非常好的解决方案,但我认为使用官方 kotlin 团队提供的库实际上会更好,因为您将向演示者类注入一个额外的参数只是为了测试。它当然按预期工作。
      猜你喜欢
      • 1970-01-01
      • 2019-11-10
      • 2019-02-10
      • 2019-09-27
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2012-04-18
      • 1970-01-01
      相关资源
      最近更新 更多