核心思路是不真正连接数据库,而是用Mock替身模拟DataSource、Connection、Statement、ResultSet等对象行为;常用Mockito逐层打桩,Spring Boot中推荐@MockBean替换JdbcTemplate或DataSource,H2内存库非Mock而是真实执行SQL。

在 Java 单元测试中,Mock 数据库连接与查询结果的核心思路是:**不真正连接数据库,而是用测试替身(Mock)模拟 DataSource、Connection、Statement、ResultSet 等关键对象的行为**。常用工具是 Mockito(配合 JUnit),对底层 JDBC 对象逐层打桩;更轻量或面向 DAO 层的场景,也可用 Mockito + @MockBean(Spring Boot Test) 或 H2 内存数据库替代真实 DB(非 Mock,但常被混淆)。下面分场景说明实用做法。
用 Mockito 手动 Mock JDBC 原生对象(适合纯 JDBC 或轻量框架)
适用于没有 Spring、直接使用 DriverManager 或自定义 DataSource 的场景。关键在于按调用链顺序 Mock:DataSource → Connection → Statement → ResultSet。
- 先
@Mock一个DataSource,让它返回你控制的Connection -
ConnectionMock 后,设定prepareStatement()返回你准备好的Statement -
Statement的executeQuery()返回预设的ResultSet -
ResultSet需 Mocknext()(控制是否有一行)、getString("col")等 getter 方法返回指定值
示例片段:
DataSource dataSource = Mockito.mock(DataSource.class);
Connection conn = Mockito.mock(Connection.class);
PreparedStatement stmt = Mockito.mock(PreparedStatement.class);
ResultSet rs = Mockito.mock(ResultSet.class);
Mockito.when(dataSource.getConnection()).thenReturn(conn);
Mockito.when(conn.prepareStatement(Mockito.anyString())).thenReturn(stmt);
Mockito.when(stmt.executeQuery()).thenReturn(rs);
Mockito.when(rs.next()).thenReturn(true).thenReturn(false); // 第一次 true(有数据),第二次 false(遍历结束)
Mockito.when(rs.getString("name")).thenReturn("Alice");
用 @MockBean(Spring Boot Test)替换 Bean(推荐用于 Spring 应用)
如果你用 Spring Boot,且 DAO 依赖 JdbcTemplate 或 DataSource,优先用 @MockBean 替换 Spring 容器中的 Bean。它自动集成 Mockito,无需手动管理生命周期。
立即学习“Java免费学习笔记(深入)”;
- 在测试类上加
@SpringBootTest和@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)(禁用自动 H2) - 用
@MockBean DataSource dataSource,然后像上面一样链式 Mock 其行为 - 若 DAO 使用
JdbcTemplate,可直接@MockBean JdbcTemplate template,MockqueryForObject()或query()方法返回测试数据
例如:
@MockBean
JdbcTemplate jdbcTemplate;
@Test
void testFindUserById() {
User expected = new User(1L, "Bob");
Mockito.when(jdbcTemplate.queryForObject(
Mockito.anyString(),
Mockito.any(RowMapper.class),
Mockito.eq(1L)
)).thenReturn(expected);
User actual = userDao.findById(1L);
assertEquals(expected, actual);
}
慎用 H2 内存库:不是 Mock,但常被误当作“轻量替代”
H2 是真实执行 SQL 的内存数据库,不属于 Mock——它不拦截调用,而是真建表、真执行、真返回结果。优点是 SQL 逻辑可端到端验证;缺点是启动慢、SQL 方言可能和生产库不一致、容易掩盖 N+1 或事务问题。
- 仅当需要验证 SQL 语法、简单 CRUD 流程,且不追求纯单元测试隔离性时选用
- 务必配
spring.datasource.url=jdbc:h2:mem:testdb和spring.sql.init.schema-locations=classpath:schema.sql初始化结构 - 避免在“单元测试”命名或场景中混用 H2,否则会模糊测试边界
进阶建议:封装 Mock 工具类或使用 jOOQ/MyBatis 的测试支持
重复写四层 Mock 易出错。可封装通用方法,比如 MockResultSet.withRow("name", "Tom").andRow("name", "Jerry");或者利用框架能力:
- jOOQ 提供
MockFileDatabase或MockConnection,支持 SQL 字符串匹配并返回预设结果集 - MyBatis 可结合
SqlSessionTemplate+MockitoMockSqlSession,或使用mybatis-mock等插件 - 若用 Spring Data JPA,
@DataJpaTest默认配 H2,如需 Mock,仍应回归@MockBean JpaRepository并 stub 方法


















