如何验证assertThrows中catch块内的对象函数已被调用?
问题描述
我有一个需要测试异常处理逻辑的函数:
fun something() { try { doSomething(input) } catch (e: SomeException) { someObject.evalSomeException(e) } } object someObject { fun evalSomeException (e: SomeException) { throw SomeOtherException() } }
我尝试用verify()验证someObject.evalSomeException被调用一次,但遇到问题。我的测试代码如下:
private val mockSomeObject = mock<SomeObject>() assertThrows<SomeOtherException>{ something(inputThatCausesSomeException) } verify(mockSomeObject, times(1)).evalSomeException(SomeException())
将verify(someObject, times(1)).evalSomeException(SomeException())放在assertThrows外部时,提示“该mock无交互”;放在内部时,verify代码未执行;尝试过spy也无法生效,请问该如何解决?
解决方案
核心问题:你代码里的
someObject是Kotlin单例(object关键字声明),测试中创建的mockSomeObject和业务代码里实际调用的单例实例完全不是同一个对象,所以verify永远检测不到交互。最优解决方法:重构业务代码,改用依赖注入,让单例变成可注入的类,避免硬编码依赖单例:
- 重构业务代码:
// 把原来的顶层函数放到类里,通过构造器注入SomeObject依赖 class BusinessService(private val someObject: SomeObject) { fun something(input: Any) { try { doSomething(input) } catch (e: SomeException) { someObject.evalSomeException(e) } } } // 把object改成普通class,不再是单例 class SomeObject { fun evalSomeException(e: SomeException) { throw SomeOtherException() } } - 编写正确的测试代码:
private val mockSomeObject = mock<SomeObject>() // 把mock注入到业务服务实例中 private val businessService = BusinessService(mockSomeObject) @Test fun testExceptionHandling() { // 先模拟doSomething抛出指定异常(如果doSomething是可mock的依赖,同样要注入) val testException = SomeException() whenever(doSomething(any())).thenThrow(testException) // 执行方法,捕获预期的异常 assertThrows<SomeOtherException> { businessService.something(inputThatCausesSomeException) } // 验证mock方法被调用,注意用eq匹配实际抛出的异常实例,或者用any() verify(mockSomeObject, times(1)).evalSomeException(eq(testException)) // 如果不需要匹配具体异常实例,也可以用: // verify(mockSomeObject, times(1)).evalSomeException(any<SomeException>()) }
- 重构业务代码:
如果你暂时无法重构代码(比如遗留系统),可以用PowerMock这类工具来mock Kotlin单例,但这种方式属于hack手段,会增加测试复杂度,不推荐长期使用。
额外注意点:
- 不要直接用
verify(..., evalSomeException(SomeException())),因为你new的这个异常和业务代码中实际抛出的不是同一个对象,mockito会认为调用不匹配,要用eq()或any()来做参数匹配。 assertThrows只是捕获异常,不会阻止后续代码执行,之前放在外部没生效的核心原因还是mock实例不匹配,和位置无关。
- 不要直接用
内容的提问来源于stack exchange,提问作者user2883071
相关产品推荐
相关产品推荐

