Mockito中JdbcTemplate结合ParameterizedRowMapper返回空列表问题排查
问题分析与解决方案
核心问题
- 方法重载不匹配:你DAO中调用的是
JdbcTemplate.query(String sql, RowMapper<T>)(两个参数),但测试代码里Mock的是三个参数的query(String, Object[], RowMapper/ResultSetExtractor)重载,导致Mock规则未触发,JdbcTemplate返回默认的空列表。 - 直接返回列表跳过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
相关产品推荐
相关产品推荐

