10.2 — Mock 与 Testcontainers
定位: Spring 测试中的 Mock 技术和真实中间件集成 面试高频度: ⭐⭐⭐ 考查方式: @MockBean vs @SpyBean、Testcontainers 使用场景
一、这是什么?为什么需要它?
@MockBean/@SpyBean 将 Mockito Mock 注入到 ApplicationContext,替代真实 Bean。Testcontainers 在测试中使用 Docker 运行真实数据库/中间件。
二、原理拆解
2.1 @MockBean
java
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean // 创建 Mock 并替换容器中的 UserService Bean
private UserService userService;
@Test
void testCreateUser() {
given(userService.create(any(UserCreateRequest.class)))
.willReturn(new User(1L, "Alice"));
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content("{\"name\":\"Alice\"}"))
.andExpect(status().isCreated());
}
}2.2 @MockBean vs @SpyBean
| 特性 | @MockBean | @SpyBean |
|---|---|---|
| 默认行为 | 所有方法返回默认值 | 调用真实方法 |
| 需要打桩 | 所有方法需手动定义 | 仅需指定方法 |
| 验证真实调用 | 需 verify | 默认真实调用 |
| 适用场景 | 完全 Mock 外部依赖 | 部分 Mock,部分真实 |
2.3 Testcontainers 集成
java
@Testcontainers
@SpringBootTest
class UserRepositoryTC {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
}Testcontainers vs H2
| 对比项 | Testcontainers | H2 |
|---|---|---|
| 数据库 | 真实数据库 | 内存数据库 |
| SQL 兼容性 | 100% 一致 | 可能有差异 |
| 启动速度 | 慢 | 快 |
| 适用场景 | CI/CD、集成测试 | 单元测试 |
| 资源占用 | 高 | 低 |
推荐:单元测试用 H2,集成测试用 Testcontainers。
三、面试视角
| 追问 | 答案要点 |
|---|---|
| @MockBean 和 @Mock 的区别? | @MockBean 将 Mock 注入到 ApplicationContext,@Mock 是普通 Mockito Mock |
| @MockBean 和 @SpyBean 的不同? | MockBean 全部打桩,SpyBean 部分打桩+部分真实 |
| Testcontainers 相比 H2 的优势? | SQL 100% 兼容,能发现 H2 无法发现的方言问题 |
📚 相关链接
- **集成测试与切片测试** — Spring Boot 测试基础
- ← 返回 **返回 测试与质量索引**