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

Mockito中JdbcTemplate结合ParameterizedRowMapper返回空列表问题排查

问题分析与解决方案

核心问题

  1. 方法重载不匹配:你DAO中调用的是JdbcTemplate.query(String sql, RowMapper<T>)(两个参数),但测试代码里Mock的是三个参数的query(String, Object[], RowMapper/ResultSetExtractor)重载,导致Mock规则未触发,JdbcTemplate返回默认的空列表。
  2. 直接返回列表跳过RowMapper执行:使用thenReturn直接返回列表时,Mockito不会调用传入的RowMapper实例,自然不会进入mapRow方法。

解决方案

1. 修正Mock的方法重载,匹配实际调用

首先确认DAO中getJdbcTemplate().query()的实际签名:

  • 如果是无参数SQL(调用query(String, RowMapper)),修正Mock规则为:
Mockito.when(mockTemplate.query(Mockito.anyString(), 
                                ArgumentMatchers.<RowMapper<MandatoryAssociationsDTO>>any()))
       .thenReturn(expectedresultList);
  • 如果是带参数SQL(调用query(String, Object[], RowMapper)),则匹配三个参数的重载:
Mockito.when(mockTemplate.query(Mockito.anyString(), 
                                Mockito.any(Object[].class),
                                ArgumentMatchers.<RowMapper<MandatoryAssociationsDTO>>any()))
       .thenReturn(expectedresultList);

2. 触发RowMapper的mapRow方法执行

若需要测试mapRow中的业务逻辑,不能直接返回列表,需用thenAnswer模拟ResultSet并调用RowMapper:

Mockito.when(mockTemplate.query(Mockito.anyString(), 
                                ArgumentMatchers.<RowMapper<MandatoryAssociationsDTO>>any()))
       .thenAnswer(invocation -> {
           // 获取传入的RowMapper实例
           RowMapper<MandatoryAssociationsDTO> rowMapper = invocation.getArgument(1);
           List<MandatoryAssociationsDTO> result = new ArrayList<>();
           
           // Mock ResultSet并模拟字段值
           ResultSet mockRs = Mockito.mock(ResultSet.class);
           // 示例:模拟plan_id字段返回值
           Mockito.when(mockRs.getInt("plan_id")).thenReturn(1234);
           // 根据需要添加更多字段的Mock规则
           
           // 模拟多条数据,调用mapRow生成结果
           for (int i = 0; i < 4; i++) {
               result.add(rowMapper.mapRow(mockRs, i));
           }
           return result;
       });

3. 优化测试类的Mock方式

避免使用Spy真实对象,直接用@Mock注解创建依赖,更简洁且减少耦合:

@RunWith(MockitoJUnitRunner.class)
public class AToolControllerMockitoTest {

    @InjectMocks
    private AToolController aToolController;

    @Mock
    private AToolService ptService;

    @Mock
    private AToolDAOImpl ptDAO;

    @Mock
    private JdbcTemplate mockTemplate;

    @Mock
    private MandatoryAssociationsDTO mandatoryAssociationsDTO;

    @Before
    public void init() {
        MockitoAnnotations.openMocks(this);
        ptDAO.setJdbcTemplate(mockTemplate);
        ptService.setPtDAO(ptDAO);
        aToolController.setPtService(ptService);
    }

    @Test
    public final void testDoMandatoryAssocaitionsist() throws AppException {
        // 准备入参Mock
        Mockito.when(mandatoryAssociationsDTO.getPlan_id()).thenReturn(1234);
        Mockito.when(mandatoryAssociationsDTO.getSortOrder()).thenReturn("ASC");

        // 准备预期结果
        List<MandatoryAssociationsDTO> expectedresultList = new ArrayList<>();
        for (int i = 0; i < 4; i++) {
            expectedresultList.add(new MandatoryAssociationsDTO());
        }

        // 配置JdbcTemplate的Mock规则(以两个参数的重载为例)
        Mockito.when(mockTemplate.query(Mockito.anyString(), 
                                        ArgumentMatchers.<RowMapper<MandatoryAssociationsDTO>>any()))
               .thenReturn(expectedresultList);

        // 执行测试
        ExtDTO extDTO = aToolController.getMandatoryAssociationsList(mandatoryAssociationsDTO);

        // 断言结果
        assertEquals(4, ((List<MandatoryAssociationsDTO>) extDTO.getData()).size());
    }
}

关键注意点

  • 必须严格匹配JdbcTemplate.query的方法重载(参数数量、类型一致),否则Mock规则不会生效。
  • 若要覆盖mapRow中的业务逻辑,必须通过thenAnswer手动触发RowMapper的调用,而非直接返回列表。
  • 使用@Mock替代Spy真实对象,能避免真实代码的副作用,让测试更可控。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 23:57:27