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

如何用Spock测试不向外抛出异常的Java catch代码块

如何测试捕获DataAccessException并仅记录日志的catch代码块?

问题背景

我需要测试以下Java代码中的catch代码块,该catch块捕获DataAccessException后仅记录日志,不向外抛出异常。

Java业务代码:

class ErrorTransImpl {
  private static final Logger logger = Logger.getLogger(ErrorTransImpl.class);

  public int errorCatcher(ErrorTrans transError){
    int ct = 0;
    if (transError != null){
      String query = "INSERT INTO tab_1 (rw1,rw2,rw3,rw4,rw5) VALUES (?,?,?,?,?)";
      try {
        ct =  jdbcTemplate.update(query, new Object[] {transError.col1(),transError.col2(), transError.col3(),transError.col4(),transError.col5()});
      }catch (DataAccessException ex) {
        logger.error(ex);
      }
    }
    return ct;
  }
}

我尝试编写了如下Spock测试代码,但遇到两个问题:1. 无法进入catch代码块;2. 即使进入catch块,也不知道该如何验证逻辑。

Spock测试代码:

def 'throw DataAccess Exception upon incorrect update'() {
  given:
  def log = Mock(Logger)
  def originalLogger = ErrorTransImpl.logger
  ErrorTransImpl.logger = log
  ErrorTransImpl errTransImpl = Spy() {
    jdbcTemplate >> {
      throw new DataAccessException() {
        @Override
        String getMessage() {
          return super.getMessage()
        }
      }
    }
  }

  when:
  errTransImpl.errorCatcher(new ErrorTrans())

  then:
  //            thrown DataAccessException
  //Not sure what to assert or test here ??
}    

解决方案

1. 修正无法进入catch块的问题

原测试中通过Spy mock jdbcTemplate的方式错误,应该直接mock JdbcTemplate实例并注入到ErrorTransImpl,指定其update方法抛出异常。同时要确保传入的ErrorTrans实例有效,避免空指针干扰测试。

2. 完整可运行的测试代码

def 'test catch DataAccessException and log error'() {
  given:
  // 1. Mock日志对象,用于验证日志行为
  def mockLogger = Mock(Logger)
  def originalLogger = ErrorTransImpl.logger
  ErrorTransImpl.logger = mockLogger

  // 2. Mock JdbcTemplate,让update方法抛出指定异常
  def mockJdbcTemplate = Mock(JdbcTemplate)
  def errTransImpl = new ErrorTransImpl()
  // 注:若ErrorTransImpl的jdbcTemplate是私有成员,建议改为构造/Setter注入,方便测试
  errTransImpl.jdbcTemplate = mockJdbcTemplate

  // 3. 准备测试用的ErrorTrans实例(若col方法需要返回值,可mock或直接赋值)
  def testTrans = new ErrorTrans()

  when:
  int result = errTransImpl.errorCatcher(testTrans)

  then:
  // 验证jdbcTemplate的update方法被调用一次,且抛出异常
  1 * mockJdbcTemplate.update(_ as String, _ as Object[]) >> { 
    throw new DataAccessException("Test DB exception") {} 
  }
  // 验证logger.error被调用一次,参数为抛出的异常
  1 * mockLogger.error(_ as DataAccessException)
  // 验证方法返回初始值0(catch块未修改ct)
  result == 0
  // 确保没有异常向外抛出
  noExceptionThrown()

  cleanup:
  // 恢复原日志对象,避免影响其他测试
  ErrorTransImpl.logger = originalLogger
}

关键验证点说明

  • 日志行为验证:通过mock Logger,断言logger.error被调用且参数正确,确认catch块逻辑执行。
  • 返回值验证:catch块未修改ct的初始值0,因此方法最终返回0,这是业务逻辑的预期结果。
  • 异常不抛出验证:用noExceptionThrown()确保异常被正确捕获,不会向外传播。
  • 资源清理:在cleanup块恢复原日志对象,避免测试污染全局状态。

针对原测试的优化点

  • 替换Spy为直接mock JdbcTemplate并注入,更符合测试规范,避免Spy的误用。
  • 补充了核心验证逻辑:日志调用、返回值、异常捕获结果,完整覆盖catch块的测试需求。

内容的提问来源于stack exchange,提问作者PravMan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 14:40:51