【问题标题】:How to unit test kotlin coroutine with nested suspended functions如何使用嵌套的挂起函数对 kotlin 协程进行单元测试
【发布时间】:2018-09-16 18:10:44
【问题描述】:

下面是我尝试进行单元测试的一段代码。 async(CommonPool) 第一个挂起函数之前的代码是可测试的,但在那之后,测试继续失败。我尝试使用runBlocking,但无论如何都无法测试嵌套的挂起异步函数。

interface Listener {
        fun onLoading(user: User?)

        fun onSuccess(user: User)
}

execute(listener: Listener) {

        listener.onLoading(null)
        val service = UserService.getInstance(context, "someurl")
        val database = UserDatabase.getInstance(context)

        launch(UI) {

            val user = async(CommonPool) {
                userDatabase.getUser()
            }.await()

            listener.onLoading(user)

            val response = service.getUsersSelf(oauthToken).await()

            async(CommonPool) {database.saveUser(userResponse.data.user)}.await()

            val user = async(CommonPool) {database.getUser()}.await()

            listener.onSuccess(user)
        }
    }

下面是我的单元测试,我使用 mockito 来模拟我的监听器并检查交互次数。

@Test
    fun execute() {
        runBlocking {
            userDatabase.saveUser(user)

            val listener = mock(GetUser.Listener::class.java)

            getUser.execute(listener)

            verify(listener, times(1)).onLoading(null) // Success

            verify(listener, times(1)).onLoading(user) // Fails

            verify(listener, times(1)).onSuccess(user) // Fails
        }
    }

但最后两个verify 测试失败。谁能帮我测试一下?

【问题讨论】:

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


    【解决方案1】:

    您的execute 是一个即发即弃的功能。如果这是用线程完成的,它将是Thread { <code> }.start() 并完全放弃对线程的引用。您唯一剩下的就是 Listener 实例,它会在调用完成时收到通知——但在发生错误时不会收到通知。

    虽然我怀疑这是使用协程的好方法,但协程的全部目的都是为了消除 回调接口,如您的 Listener,如果您知道这种方法对您有好处,那么监听器是您唯一的处理你启动的协程中发生的事情。

    因此,您必须在侦听器中实现单元测试逻辑。鉴于没有失败回调,您可能会等待一些有限的时间来调用方法。一种方法:

    fun execute() {
        val latch = CountDownLatch(3)
        runBlocking {
            val x = object : Listener {
    
                override fun onLoading(user: User?) {
                    if (user == null) {
                        require(latch.count == 3L)
                        latch.countDown()
                    } else {
                        require(latch.count == 2L)
                        latch.countDown()
                    }
                }
    
                override fun onSuccess(user: User) {
                    require (latch.count == 1L)
                    latch.countDown()
                }
            }
        }
        require(latch.await(10, TimeUnit.SECONDS))
    }
    

    【讨论】:

      【解决方案2】:

      您可以在测试时使用依赖注入将 UI Dispatcher 替换为 Dispatchers.Unconfined。此测试成功:

      @Test
      fun test() {
          var result = false
          GlobalScope.launch (Dispatchers.Unconfined) {
              async(Dispatchers.Unconfined) {
                  sleep(1000)
                  result = true
              }.await()
          }
          assertTrue(result)
      }
      

      【讨论】:

        猜你喜欢
        • 2018-06-15
        • 1970-01-01
        • 1970-01-01
        • 2018-05-13
        • 2021-10-23
        • 2018-04-20
        • 2019-09-09
        • 1970-01-01
        • 2018-06-13
        相关资源
        最近更新 更多