如何用Mockito验证PreparedStatement场景?测试类无法mock且进不去PreparedStatementCreator块
单测中无法触发PreparedStatementCreator代码块的问题排查与实现
核心问题成因
你当前编写的测试代码完全没有命中业务运行时的逻辑入口,具体原因如下:
- 对象实例不匹配导致mock桩失效:业务代码在运行时会现场创建一个新的匿名
PreparedStatementCreator实例传给jdbcTemplate.update,而你在测试类里提前new了另一个匿名实现类实例作为mock桩的参数。两个匿名类实例没有重写equals逻辑,对Mockito来说是完全不同的对象,永远不可能匹配上,对应的桩根本不会生效。 - 动态参数硬编码导致匹配失败:你把
Timestamp.from(Instant.now())直接写在了mock桩的参数里,测试类生成时间戳、业务代码生成时间戳是两个不同时间点的操作,得到的时间戳对象值不相等,即使其他参数匹配,这部分的update桩也不会命中。 - 多余的mock对象完全没被使用:你声明的
@Mock PreparedStatementCreator statementcreator对象,在业务代码里从来没有被引用过,业务逻辑用的是自己new的匿名实现,这个mock对象完全不会参与到流程中,自然没法触发你预期的代码块。
正确的单测实现步骤
- 替换无效的硬编码参数匹配逻辑,用Mockito的匹配器适配动态生成的参数
// 匹配第一个带SQL字符串的update调用,所有动态值用any匹配器,固定值用eq校验 Mockito.when(jdbcTemplate.update( ArgumentMatchers.eq(UPDATE_DEPLOYMENT_BY_REQUEST_ID_FOR_APPROVE_AND_REJECT), ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.any(Timestamp.class), ArgumentMatchers.eq("123445") )).thenReturn(1); // 匹配任意PreparedStatementCreator类型的update调用 Mockito.when(jdbcTemplate.update(ArgumentMatchers.<PreparedStatementCreator>any())).thenReturn(1); - 补全连接、预编译语句的mock桩,为触发Creator内部逻辑做准备
// 当业务代码通过连接创建插入审批记录的预编译语句时,返回你提前mock的PreparedStatement对象 Mockito.when(connection.prepareStatement(INSERT_RECORD_INTO_DEPLOYMENT_APPROVAL)).thenReturn(statement); - 运行业务方法,捕获业务代码真实传入的
PreparedStatementCreator实例,手动触发它的方法进入你想要验证的代码块// 先执行业务测试方法 String response = utilimpl.approverReject(requestDto, errors); // 定义参数捕获器,拿到传给jdbcTemplate.update的真实Creator实例 ArgumentCaptor<PreparedStatementCreator> creatorCaptor = ArgumentCaptor.forClass(PreparedStatementCreator.class); Mockito.verify(jdbcTemplate).update(creatorCaptor.capture()); // 手动调用真实Creator的createPreparedStatement方法,进入你之前一直没法触发的代码块 PreparedStatement realPs = creatorCaptor.getValue().createPreparedStatement(connection); // 验证Creator内部对PreparedStatement的参数设置是否符合业务预期 Mockito.verify(statement).setString(1, requestDto.getApprover()); Mockito.verify(statement).setString(2, "123445"); - 最后验证业务返回值是否符合预期即可。
避坑提示
- 所有包含动态生成值(当前时间、随机数、外部系统返回的不确定对象)的mock参数,不要直接硬编码具体值,优先用
any()类匹配器,固定值用eq()明确校验。 - 不要手动创建匿名实现类当mock桩的匹配参数,对接口的匿名实现类,直接用
any()匹配,配合捕获器拿到真实实例验证逻辑即可。 - 不要依赖你自己声明的、没被业务代码引用的mock对象写桩逻辑,所有mock桩的逻辑都要对应业务代码真实会调用的对象。
内容的提问来源于stack exchange,提问作者Mani
相关产品推荐
相关产品推荐

