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

如何用Mockito编写基于NamedParameterJdbcTemplate的DAO层JUnit测试用例

用Mockito编写DAO层测试用例(基于NamedParameterJdbcTemplate)

首先贴出原DAO层代码:

@Repository
public class GeneratorRepository {

    @Value("${sql.existingUtQuery}")
    private String existingUtQuery;

    @Autowired
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Autowired
    private BeanPropertyRowMapper<Generator> generatorRowMapper;

    public Generator findExistingUt(Request request) {
        Generator ut = new Generator();
        try {
            SqlParameterSource namedParameters = new MapSqlParameterSource()
                    .addValue("no", request.getId(), Types.VARCHAR)
                    .addValue("sub", request.getSub(), Types.VARCHAR)
                    .addValue("rt", request.getRt(), Types.INTEGER)
                    .addValue("sta", request.getSta(), Types.VARCHAR);
            ut = namedParameterJdbcTemplate.queryForObject(existingUtQuery, namedParameters, generatorRowMapper);
        } catch (EmptyResultDataAccessException e) {
            ut = null;
        } catch (Exception e) {
            throw e;
        }
        return ut;
    }
}

你的测试用例存在几个问题:

  • Mock方法参数错误:when中第二个参数误传为namedParameterJdbcTemplate,实际应匹配SqlParameterSource类型参数
  • 未注入DAO依赖的SQL语句:DAO中SQL从配置文件读取,测试时需手动设置该值
  • 未覆盖全场景:仅测试正常返回情况,遗漏空结果、异常抛出场景
  • Generator类未重写equals:直接用assertEquals会因引用比对导致断言失败

完整测试用例

基础注解与依赖配置

使用Mockito注解管理Mock对象,通过反射注入DAO的SQL属性:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.EmptyResultDataAccessException;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class GeneratorRepositoryTest {

    @InjectMocks
    private GeneratorRepository generatorRepo;

    @Mock
    private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Mock
    private BeanPropertyRowMapper<Generator> generatorRowMapper;

    private final String TEST_SQL = "select Top 1 * from existingUT where NO= :no AND SUB= :sub AND rt= :rt and STA= :sta order by LAST_UPDATE desc";

    public GeneratorRepositoryTest() {
        // 手动设置DAO中@Value注入的SQL属性
        org.springframework.test.util.ReflectionTestUtils.setField(generatorRepo, "existingUtQuery", TEST_SQL);
    }

场景1:查询到数据,正常返回对象

@Test
    public void findExistingUt_WhenDataExists_ReturnsGenerator() {
        Request request = new Request("12345", "F", 0, "XC");
        Generator expectedResponse = new Generator("12345", "F", 0, "XC","12345.FCST","345TDGHDF45G","OS01",null);

        // Mock queryForObject方法,匹配对应参数
        when(namedParameterJdbcTemplate.queryForObject(
                eq(TEST_SQL),
                any(SqlParameterSource.class),
                eq(generatorRowMapper)
        )).thenReturn(expectedResponse);

        Generator actualResponse = generatorRepo.findExistingUt(request);

        // 断言结果并验证方法调用次数
        assertEquals(expectedResponse, actualResponse);
        verify(namedParameterJdbcTemplate, times(1)).queryForObject(
                eq(TEST_SQL),
                any(SqlParameterSource.class),
                eq(generatorRowMapper)
        );
    }

场景2:无查询结果,返回null

@Test
    public void findExistingUt_WhenNoData_ReturnsNull() {
        Request request = new Request("99999", "X", 1, "XX");

        // Mock抛出空结果异常
        when(namedParameterJdbcTemplate.queryForObject(
                eq(TEST_SQL),
                any(SqlParameterSource.class),
                eq(generatorRowMapper)
        )).thenThrow(new EmptyResultDataAccessException(1));

        Generator actualResponse = generatorRepo.findExistingUt(request);

        assertNull(actualResponse);
        verify(namedParameterJdbcTemplate, times(1)).queryForObject(
                eq(TEST_SQL),
                any(SqlParameterSource.class),
                eq(generatorRowMapper)
        );
    }

场景3:查询抛出异常,向上传递

@Test(expected = RuntimeException.class)
    public void findExistingUt_WhenExceptionThrown_ThrowsException() {
        Request request = new Request("11111", "Y", 2, "YY");

        // Mock抛出运行时异常
        when(namedParameterJdbcTemplate.queryForObject(
                eq(TEST_SQL),
                any(SqlParameterSource.class),
                eq(generatorRowMapper)
        )).thenThrow(new RuntimeException("数据库查询失败"));

        generatorRepo.findExistingUt(request);
    }
}

关键注意事项

  • @InjectMocks:自动将Mock对象注入到DAO实例中,无需手动初始化依赖
  • 参数匹配:用eq()匹配具体值,any()匹配任意类型参数,确保Mockito正确识别方法调用
  • Generator类重写equals:必须重写equals和hashCode方法,否则assertEquals会比对对象引用而非内容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 12:42:42