集成测试:内存库/事务回滚/Testcontainers 简介
上一章的切片测试只验证单层,而 Repository 与数据库、Service 与事务的真实配合要靠集成测试。Spring Boot 集成测试常见三件套:内存库(快但方言有差异)、事务回滚(测试互不污染)、Testcontainers(用 Docker 拉起与生产一致的真实中间件)。
内存库:H2 + @AutoConfigureTestDatabase
测试作用域引入 H2,切片注解会自动用内置库替换真实数据源:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
@DataJpaTest // 切片:JPA Repository + H2 + 自带 @Transactional
class UserRepositoryTest {
@Autowired
UserRepository userRepository;
@Test
void saveAndFind_shouldWork() {
User u = userRepository.save(new User("张三"));
// 输出:保存成功,主键由数据库生成
Assertions.assertThat(u.getId()).isNotNull();
Assertions.assertThat(userRepository.findByUsername("张三")).isPresent();
}
}
@DataJpaTest 自带事务:每个测试方法结束后整体回滚,所以上一个用例写入的数据不会泄漏给下一个用例。
@SpringBootTest 全量集成与回滚
需要跨层验证时用全量上下文,同样可以"内存库 + 事务回滚":
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) // 用 H2 顶替配置里的数据源
@Transactional // 每个测试方法结束时回滚
class OrderServiceIT {
@Autowired
OrderService orderService;
@Test
void createOrder_shouldPersist() {
orderService.create(new OrderCreateRequest(1L, 2));
// 输出:方法内能查到刚创建的订单(同事务),测试结束后数据回滚
}
}
注意两个失效场景:被测代码内部开了 REQUIRES_NEW 传播级别、或把操作丢进新线程/异步执行,都会脱离测试事务,回滚随之失效。
Testcontainers 简介
H2 与真实 MySQL/PG 在锁、函数、方言上总有差异,"像生产一样测"用 Testcontainers:测试前用 Docker 启动一次性容器,用完即销毁。
@Testcontainers
class UserRepositoryTC {
@Container
@ServiceConnection // Spring Boot 3.1+:把容器连接信息自动注入 DataSource
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16-alpine");
@Autowired
UserRepository userRepository;
@Test
void crud_againstRealPg() {
userRepository.save(new User("张三"));
// 输出:真实 PostgreSQL 上执行,断言不再受 H2 方言限制
}
}
配套依赖 org.testcontainers:junit-jupiter、org.testcontainers:postgresql 与 spring-boot-testcontainers,版本由 Boot BOM 管理。实战要点:CI 必须能跑 Docker;容器声明为 static 单例并在多测试类间复用可显著提速;镜像版本尽量与生产一致。
小结:优先"切片 + H2"跑快反馈,核心链路用 @SpringBootTest 事务回滚隔离,需要真实中间件行为时上 Testcontainers + @ServiceConnection。