如何在Mockk中测试异常抛出及日志方法调用?
问题解决:CoroutineScope协程测试中verify断言失败
问题原因
你的stop()方法使用了**独立的CoroutineScope(Dispatchers.IO)**启动协程,而runTest默认仅跟踪测试自身作用域内的协程。advanceUntilIdle()无法等待这个外部自定义作用域的协程执行完成,导致断言时logError方法还没被调用,从而触发验证失败。
解决方案
方案1:注入CoroutineScope(推荐,解耦测试与业务代码)
将CoroutineScope作为类的依赖注入,测试时使用runTest提供的TestScope替换,让测试能完全控制协程执行。
修改业务代码:
class YourClass( // 提供默认实现,不影响生产代码 private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO), private val internalMqttClient: InternalMqttClient, private val internalLogger: InternalLogger ) { fun stop() { println("----") coroutineScope.launch { try { internalMqttClient.disconnectForcibly() } catch (e: Exception) { internalLogger.logError(DEFAULT_LOG_TAG, "1111") } } } }
测试代码:
@Test fun test2() = runTest { // 使用当前测试的TestScope注入到目标类 val testScope = this val subject = YourClass( coroutineScope = testScope, internalMqttClient = mock(), internalLogger = mock() ) every { subject.internalMqttClient.disconnectForcibly() } throws Exception() subject.stop() advanceUntilIdle() // 现在能等待TestScope内的协程执行完毕 verify(exactly = 1) { subject.internalLogger.logError( DEFAULT_LOG_TAG, "1111" ) } }
方案2:替换Dispatchers.IO为测试调度器
通过替换IO调度器为runTest提供的测试调度器,让自定义作用域的协程跑在可控制的调度器上。
测试代码修改:
@Test fun test2() = runTest { val testDispatcher = this.testDispatcher // 替换IO调度器为测试调度器 Dispatchers.setIO(testDispatcher) try { every { internalMqttClient.disconnectForcibly() } throws Exception() subject.stop() advanceUntilIdle() // 此时能等待IO调度器上的协程执行完成 verify(exactly = 1) { internalLogger.logError( DEFAULT_LOG_TAG, "1111" ) } } finally { // 重置调度器,避免影响其他测试用例 Dispatchers.resetIO() } }
方案3:手动等待自定义作用域完成(不推荐,耦合性高)
如果无法修改业务代码,可通过暴露或反射获取自定义CoroutineScope的Job,手动等待其执行完毕:
@Test fun test2() = runTest { every { internalMqttClient.disconnectForcibly() } throws Exception() subject.stop() // 假设你的类暴露了coroutineScope属性 subject.coroutineScope.joinAll() advanceUntilIdle() verify(exactly = 1) { internalLogger.logError( DEFAULT_LOG_TAG, "1111" ) } }
内容的提问来源于stack exchange,提问作者Bob Redity
相关产品推荐
相关产品推荐

