1. 概述

Spring 的 JdbcTemplate 是一个非常实用的数据访问工具,它让开发者可以专注于 SQL 编写和结果处理,而不用操心底层的连接管理、资源释放等琐事。它直接与数据库交互,执行 SQL 并封装结果。

既然它涉及数据库操作,我们通常会考虑使用集成测试来验证数据查询是否正确。但与此同时,单元测试也有其价值——它可以快速验证业务逻辑、方法流程,而无需启动数据库。

本文将介绍几种对 JdbcTemplate 相关代码进行单元测试的常用方式,帮助你在不同场景下做出合适选择。

2. JdbcTemplate 与查询执行

我们先来看一个典型的 DAO 类,它使用了 JdbcTemplate

public class EmployeeDAO {
    private JdbcTemplate jdbcTemplate;

    public void setDataSource(DataSource dataSource) {
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int getCountOfEmployees() {
        return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class);
    }
}

这个类通过依赖注入接收一个 DataSource,并在 setDataSource 方法中初始化 JdbcTemplategetCountOfEmployees() 方法则使用 JdbcTemplate 执行一条简单的 COUNT 查询。

对于这类方法的测试,主要有两种思路:

使用内存数据库(如 H2)作为数据源
真实执行 SQL,适合验证 SQL 逻辑和数据映射是否正确。

直接 Mock JdbcTemplate
跳过数据库交互,只验证方法是否正确调用了 JdbcTemplate 的 API,适合快速测试逻辑分支。

下面分别展开。

3. 使用 H2 数据库进行单元测试

我们可以构建一个指向 H2 内存数据库的 DataSource,并将其注入到 DAO 中,从而在真实(但轻量)的数据库环境中运行测试。

@Test
public void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() {
    DataSource dataSource = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql")
      .build();

    EmployeeDAO employeeDAO = new EmployeeDAO();
    employeeDAO.setDataSource(dataSource);

    assertEquals(4, employeeDAO.getCountOfEmployees());
}

关键步骤说明:

  • 使用 EmbeddedDatabaseBuilder 快速搭建 H2 内存库
  • 通过 addScript 加载建表和测试数据脚本

对应的 schema.sql

CREATE TABLE EMPLOYEE
(
    ID int NOT NULL PRIMARY KEY,
    FIRST_NAME varchar(255),
    LAST_NAME varchar(255),
    ADDRESS varchar(255)
);

test-data.sql

INSERT INTO EMPLOYEE VALUES (1, 'James', 'Gosling', 'Canada');
INSERT INTO EMPLOYEE VALUES (2, 'Donald', 'Knuth', 'USA');
INSERT INTO EMPLOYEE VALUES (3, 'Linus', 'Torvalds', 'Finland');
INSERT INTO EMPLOYEE VALUES (4, 'Dennis', 'Ritchie', 'USA');

⚠️ 踩坑提示:
如果 SQL 脚本路径不对或表名大小写不匹配(H2 默认转大写),可能导致表不存在或查询为空。建议统一使用大写表名,或在连接 URL 中添加 ;MODE=MySQL 等模式兼容配置。

这种方式适合验证复杂 SQL、JOIN、事务等真实场景,但需要维护脚本,略显繁琐。

4. 使用 Mock 对象进行单元测试

如果我们只关心方法是否正确调用了 JdbcTemplate,而不想依赖数据库,就可以使用 Mock 框架(如 Mockito)来模拟 JdbcTemplate 的行为。

public class EmployeeDAOUnitTest {
    @Mock
    JdbcTemplate jdbcTemplate;

    @Test
    public void whenMockJdbcTemplate_thenReturnCorrectEmployeeCount() {
        EmployeeDAO employeeDAO = new EmployeeDAO();
        ReflectionTestUtils.setField(employeeDAO, "jdbcTemplate", jdbcTemplate);
        Mockito.when(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EMPLOYEE", Integer.class))
          .thenReturn(4);

        assertEquals(4, employeeDAO.getCountOfEmployees());
    }
}

核心要点:

  • 使用 @Mock 创建 JdbcTemplate 的 Mock 实例
  • 通过 ReflectionTestUtils.setField() 反射注入私有字段(Spring Test 提供的工具类)
  • 使用 Mockito.when(...).thenReturn(...) 预设方法返回值

避免字符串硬匹配

上面的测试对 SQL 字符串做了完全匹配,但在实际项目中,SQL 可能是拼接的或动态生成的,硬编码字符串容易导致测试脆弱。

我们可以改用 Mockito 的参数匹配器来规避这个问题:

Mockito.when(jdbcTemplate.queryForObject(Mockito.anyString(), Mockito.eq(Integer.class)))
  .thenReturn(3);
assertEquals(3, employeeDAO.getCountOfEmployees());

✅ 推荐做法:

  • anyString() 匹配任意 SQL 字符串
  • eq(Integer.class) 确保返回类型正确

这样即使 SQL 稍有变化,只要逻辑正确,测试仍能通过。

⚠️ 注意:
过度使用 anyString() 可能掩盖 SQL 错误,建议在逻辑测试中使用,在SQL 验证测试中仍应使用具体字符串。

5. Spring Boot 中的 @JdbcTest

如果你在使用 Spring Boot,那就有更简单的办法了 —— 使用 @JdbcTest 注解。

它会自动配置一个内存数据库(默认 H2)、初始化 JdbcTemplate Bean,并只加载数据访问相关的组件,非常适合 DAO 层测试。

@JdbcTest
@Sql({"schema.sql", "test-data.sql"})
class EmployeeDAOIntegrationTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    void whenInjectInMemoryDataSource_thenReturnCorrectEmployeeCount() {
        EmployeeDAO employeeDAO = new EmployeeDAO();
        employeeDAO.setJdbcTemplate(jdbcTemplate);

        assertEquals(4, employeeDAO.getCountOfEmployees());
    }
}

优势:

  • 自动装配 JdbcTemplate
  • @Sql 注解可指定初始化脚本,简洁明了
  • 测试启动快,隔离性好

✅ 建议:
对于 Spring Boot 项目,优先考虑 @JdbcTest + @Sql 组合,简单粗暴又可靠。

6. 总结

方式 适用场景 优点 缺点
H2 内存数据库 验证 SQL 正确性 接近真实环境 需维护脚本
Mock JdbcTemplate 验证方法调用逻辑 快速、无依赖 不验证 SQL
@JdbcTest(Spring Boot) Spring Boot 项目 DAO 测试 自动化程度高 仅限 Spring Boot

选择哪种方式,取决于你的测试目标:

  • 要验证 SQL 是否正确?用 H2 或 @JdbcTest
  • 要验证 逻辑分支或异常处理?用 Mock
  • Spring Boot 项目?优先 @JdbcTest

示例代码已整理至 GitHub:https://github.com/baomidou/tutorials/tree/master/spring-jdbc-testing


原始标题:Spring JdbcTemplate Unit Testing