DAO层测试需用真实数据库行为实现可控、隔离、可重置的集成验证;H2适用于快速反馈,Testcontainers更贴近生产环境;统一事务回滚保障原子性,须规避时区、字符集、数据污染等陷阱。

DAO 层测试不能靠“绕开数据库”来实现单元化,它本质是集成验证——既要保证 SQL 正确、ORM 映射无误,又要确认事务与连接行为符合预期。关键不是回避外部依赖,而是**可控、隔离、可重置地使用真实数据库行为**。
用内存数据库快速验证基础逻辑
适合开发初期或 CI 快速反馈场景。H2 是最常用选择,配置简单、启动快、支持大部分标准 SQL。
- 在测试专用 Spring 配置(如
application-test.yml)中切换数据源为 H2,并启用spring.sql.init.mode=always自动建表 - SQL 脚本放在
src/test/resources/schema.sql和data.sql,确保每次测试前表结构和初始数据一致 - 注意规避 H2 特有语法(如
IF NOT EXISTS或 MySQL 的ON DUPLICATE KEY UPDATE),否则上线后可能出错
用 Testcontainers 还原生产环境行为
当 H2 满足不了需求(比如要测存储过程、特定索引行为、MySQL 的严格模式、时区处理或 JSON 字段操作),Testcontainers 是更可靠的选择。
- 每个测试类或方法启动一个独立的 MySQL 容器实例,端口随机分配,完全隔离
- 通过
@Container声明静态容器,在整个测试类生命周期内复用,避免反复启停开销 - 配合
@DynamicPropertySource注入真实 JDBC URL,让 Spring Boot 自动加载对应数据源 - 推荐搭配
DatabaseRider或手动执行TRUNCATE+INSERT确保测试间数据干净
用事务回滚保障测试原子性
无论用 H2 还是 Testcontainers,插入/更新类测试都应默认运行在事务中,并在方法结束后自动回滚。
立即学习“Java免费学习笔记(深入)”;
- Spring Boot 测试中加
@Transactional注解即可,无需显式 commit/rollback - 注意:DDL 操作(如
CREATE TABLE)无法回滚,所以建表必须放在测试启动阶段(schema.sql) - 若需验证提交后效果(例如触发器、binlog、缓存同步),则改用
@Commit并配合清理逻辑
避免常见陷阱
很多 DAO 测试失败不是代码问题,而是环境设计疏漏。
- 不共用测试数据库:多人并行跑测试时,H2 文件路径或 MySQL 容器名冲突会导致数据互相覆盖
- 不忽略时区与字符集:MySQL 容器默认时区是 UTC,Java 应用连的是 CST,可能导致时间字段读写偏差
- 不校验返回对象状态:只断言
list.size() == 1不够,还要检查 ID、时间戳、是否为代理对象、懒加载字段是否为 null 等 - 不覆盖边界情况:空列表、重复主键、超长字符串、NULL 值插入、批量操作异常中断等都要有对应用例


















