如何用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
相关产品推荐
相关产品推荐

