Unit testing 单元测试暂停协同程序

Unit testing 单元测试暂停协同程序,unit-testing,kotlin,kotlin-coroutines,Unit Testing,Kotlin,Kotlin Coroutines,对Kotlin来说有点新鲜并正在测试它。。。我尝试使用一个suspend方法测试dao对象包装器,该方法对SQL返回对象使用waitFirst()。然而,当我为它编写单元测试时,它只是停留在一个循环中。我认为这是因为waitfirst()不在相同的测试范围内 实施: suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): OrderDomain { var result: Map<S

对Kotlin来说有点新鲜并正在测试它。。。我尝试使用一个suspend方法测试dao对象包装器,该方法对SQL返回对象使用waitFirst()。然而,当我为它编写单元测试时,它只是停留在一个循环中。我认为这是因为waitfirst()不在相同的测试范围内

实施:

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")
        }
    }
suspend-fun查询执行(querySpec:DatabaseClient.genericexecutspec):OrderDomain{
var结果:地图?
试一试{
结果=querySpec.fetch().first().awaitFirst()
}捕获(e:例外){
如果(e是DataAccessResourceFailureException)
抛出通信异常(
“无法连接到”+DatabaseConstants.DB_NAME+
DatabaseConstants.ORDERS_TABLE+“在执行querySelect时”,
“查询选择”,
E
)
抛出InternalException(“执行SQL querySelect时遇到R2DBCEException”,e)
}
如果(结果==null)
抛出ResourceNotFoundException(“在Aurora DB中找不到资源”)
试一试{
返回OrderDomain(结果)
}捕获(e:例外){
抛出InternalException(“解析到OrderDomain实体时出现异常”,e)
}最后{
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)
    }
@测试
fun`get by orderid id,null`()=runBlocking{
//分配
Mockito.`when`(fetchSpecMock.first()).thenReturn(monoMapMock)
Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)
//行动与主张
val异常=资产抛出{
auroraClientWrapper.queryExecution(
databaseClient.sql(“从orderTable中选择*,其中orderId=:1”).bind(“1”,“123”)orderId
)
}
assertEquals(“在Aurora DB中找不到资源”,exception.message)
}
我注意到了这个问题,但没有一个解决办法对我有效

在单元测试中使用runBlocking只会导致我的测试永远无法完成。使用runBlockingTest显式抛出一个错误,表示“作业从未完成”。。。有人知道吗?在这一点上有没有黑客

另外,我很理解您不应该将suspend与块一起使用的观点,因为这有点违背了suspend的目的,因为它是释放线程以稍后继续,而不是阻塞强制线程等待结果。。。但这是怎么回事

private suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): Map {
        var result: Map<String, Any>?
        try {
            result = withContext(Dispatchers.Default) {
                querySpec.fetch().first().block()
            }
return result
}
private-suspend-fun查询执行(querySpec:DatabaseClient.genericexecutspec):映射{
var结果:地图?
试一试{
结果=withContext(Dispatchers.Default){
querySpec.fetch().first().block()
}
返回结果
}
这是否意味着withContext将使用一个新线程,并在其他地方重新使用旧线程?这不会真正优化任何内容,因为无论生成新上下文如何,我仍将有一个线程被阻止?

找到了解决方案

monoMapMock
是Mockito的模拟值。kotlinx测试协同程序似乎无法截获异步以返回mono。因此,我强制我可以模拟的方法返回一个真实的mono值,而不是模拟的mono值。为此,正如Louis所建议的。我停止模拟并返回一个真实的值

@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)
    }
@测试
fun`get by orderid id,null`()=runBlocking{
//分配
Mockito.`when`(fetchSpecMock.first()).thenReturn(Mono.empty())
Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)
//行动与主张
val异常=资产抛出{
auroraClientWrapper.queryExecution(
databaseClient.sql(“从orderTable中选择*,其中orderId=:1”).bind(“1”,“123”)orderId
)
}
assertEquals(“在Aurora DB中找不到资源”,exception.message)
}

< /代码>嘲弄协同函数可能是棘手的。考虑使用假的。第二个问题的答案是正确实现的协同程序代码暂停而不是阻塞,它不会锁定线程。(结果是,旧线程将在别处使用。)@LouisWasserman感谢您的输入,您能用一个伪函数来展开吗?问题是我仍然需要截取waitfirst()。或者您的意思是为first()截取提供一个真实值,而不是模拟值,然后让waitfirst()截取改为应用于实际值?我的意思是创建
数据库客户端.genericexecutspec
类型的实现,而不进行模拟,该类型返回在构造时传入的测试值。@LouisWasserman然而,这个genericexecutspec命中了一个DB-如果我要创建一个不进行模拟的实现,它必须是实时数据,并且nd of违背了单元测试的意图。制作一个没有命中DB的。或者制作一个假的内存DB。