You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证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永远检测不到交互。

  • 最优解决方法:重构业务代码,改用依赖注入,让单例变成可注入的类,避免硬编码依赖单例:

    1. 重构业务代码:
      // 把原来的顶层函数放到类里,通过构造器注入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()
          }
      }
      
    2. 编写正确的测试代码:
      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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 04:17:51