如何使用嵌套挂起函数对Kotlin协程进行单元测试

7
以下是我正在尝试进行单元测试的代码。在第一个挂起函数之前的代码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测试失败了。有谁能帮我进行测试吗?
2个回答

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

1
你可以在测试时使用依赖注入将UI调度程序替换为Dispatchers.Unconfined。这个测试成功了:
@Test
fun test() {
    var result = false
    GlobalScope.launch (Dispatchers.Unconfined) {
        async(Dispatchers.Unconfined) {
            sleep(1000)
            result = true
        }.await()
    }
    assertTrue(result)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接