You need to enable JavaScript to run this app. 如何用Mockito验证PreparedStatement场景?测试类无法mock且进不去PreparedStatementCreator块
优惠活动
大模型
产品
解决方案
定价
更多

如何用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对象完全不会参与到流程中,自然没法触发你预期的代码块。

正确的单测实现步骤

  1. 替换无效的硬编码参数匹配逻辑,用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);
    
  2. 补全连接、预编译语句的mock桩,为触发Creator内部逻辑做准备
    // 当业务代码通过连接创建插入审批记录的预编译语句时,返回你提前mock的PreparedStatement对象
    Mockito.when(connection.prepareStatement(INSERT_RECORD_INTO_DEPLOYMENT_APPROVAL)).thenReturn(statement);
    
  3. 运行业务方法,捕获业务代码真实传入的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");
    
  4. 最后验证业务返回值是否符合预期即可。

避坑提示

  • 所有包含动态生成值(当前时间、随机数、外部系统返回的不确定对象)的mock参数,不要直接硬编码具体值,优先用any()类匹配器,固定值用eq()明确校验。
  • 不要手动创建匿名实现类当mock桩的匹配参数,对接口的匿名实现类,直接用any()匹配,配合捕获器拿到真实实例验证逻辑即可。
  • 不要依赖你自己声明的、没被业务代码引用的mock对象写桩逻辑,所有mock桩的逻辑都要对应业务代码真实会调用的对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 02:45:10