单元测试暂停协程
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)
}
对 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)
}