单元测试暂停协程

Unit testing suspend coroutine

对 Kotlin 有点陌生并对其进行测试...我正在尝试使用挂起方法测试 dao 对象包装器,该方法对 SQL return 对象使用 awaitFirst() 。然而,当我为它编写单元测试时,它只是陷入了一个循环。我认为这是由于 awaitFirst() 不在测试的同一范围内

实施:

suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): OrderDomain {
        var result: Map<String, Any>?
        try {
            result = querySpec.fetch().first().awaitFirst()
        } catch (e: Exception) {
            if (e is DataAccessResourceFailureException)
                throw CommunicationException(
                    "Cannot connect to " + DatabaseConstants.DB_NAME +
                        DatabaseConstants.ORDERS_TABLE + " when executing querySelect",
                    "querySelect",
                    e
                )
            throw InternalException("Encountered R2dbcException when executing SQL querySelect", e)
        }

        if (result == null)
            throw ResourceNotFoundException("Resource not found in Aurora DB")

        try {
            return OrderDomain(result)
        } catch (e: Exception) {
            throw InternalException("Exception when parsing to OrderDomain entity", e)
        } finally {
            logger.info("querySelect;stage=end")
        }
    }

单元测试:

@Test
    fun `get by orderid id, null`() = runBlocking {
        // Assign
        Mockito.`when`(fetchSpecMock.first()).thenReturn(monoMapMock)
        Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)

        // Act & Assert
        val exception = assertThrows<ResourceNotFoundException> {
            auroraClientWrapper.queryExecution(
                databaseClient.sql("SELECT * FROM orderTable WHERE orderId=:1").bind("1", "123") orderId
            )
        }
        assertEquals("Resource not found in Aurora DB", exception.message)
    }

我在 https://github.com/Kotlin/kotlinx.coroutines/issues/1204 上注意到了这个问题,但 none 的解决方法对我有用...

在单元测试中使用 runBlocking 只会导致我的测试永远无法完成。使用 runBlockingTest 明确抛出一个错误,说“作业从未完成”......有人知道吗?目前有什么破解方法吗?

我也相当理解你不应该将挂起与块一起使用的观点,因为这有点违背挂起的目的,因为它释放线程以便稍后继续而不是阻塞强制线程等待结果.. .但是这是如何工作的呢?

private suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): Map {
        var result: Map<String, Any>?
        try {
            result = withContext(Dispatchers.Default) {
                querySpec.fetch().first().block()
            }
return result
}

这是否意味着 withContext 将使用新线程,并在其他地方重新使用旧线程?这并没有真正优化任何东西,因为无论产生一个新的上下文,我仍然有一个线程被阻塞?

找到解决方案。

monoMapMock 是来自 Mockito 的模拟值。似乎 kotlinx-test 协程无法拦截到 return 单声道的异步。所以我强制我可以模拟的方法 return 一个真正的单声道值而不是模拟单声道。按照 Louis 的建议这样做。我不再嘲笑它 return 一个真正的价值

@Test
    fun `get by orderid id, null`() = runBlocking {
        // Assign
        Mockito.`when`(fetchSpecMock.first()).thenReturn(Mono.empty())
        Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)

        // Act & Assert
        val exception = assertThrows<ResourceNotFoundException> {
            auroraClientWrapper.queryExecution(
                databaseClient.sql("SELECT * FROM orderTable WHERE orderId=:1").bind("1", "123") orderId
            )
        }
        assertEquals("Resource not found in Aurora DB", exception.message)
    }