单元测试实践
单元测试保障代码质量,是防御式编程的重要一环。
1. 测试框架对比
| 框架 | 特点 | 适用场景 |
|---|---|---|
| JUnit 5 | 最新标准、Lambda 支持 | Spring Boot 默认 |
| TestNG | 依赖测试、并发执行 | 复杂测试场景 |
| Mockito | Mock 框架 | 隔离依赖 |
| AssertJ | 流式断言 | 易读断言 |
| Spring Test | Spring 上下文支持 | Spring 项目 |
2. 常用注解
@SpringBootTest
@DisplayName("用户服务测试")
class UserServiceTest {
@MockBean
private UserMapper userMapper;
@Autowired
private UserService userService;
@Test
@Order(1)
@DisplayName("创建用户 - 成功")
void createUser_success() {
// given
UserRequest req = new UserRequest();
req.setUsername("test");
req.setMobile("13800138000");
when(userMapper.insert(any())).thenReturn(1);
// when
User result = userService.createUser(req);
// then
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("test");
verify(userMapper, times(1)).insert(any());
}
@Test
@DisplayName("创建用户 - 用户名重复")
void createUser_duplicateUsername() {
// given
when(userMapper.findByUsername("test")).thenReturn(existingUser);
// when & then
assertThatThrownBy(() -> userService.createUser(req))
.isInstanceOf(BusinessException.class)
.hasMessageContaining("用户名已存在");
}
}
3. 测试分类
| 类型 | 说明 | 占比建议 |
|---|---|---|
| 单元测试 (UT) | 测试单个方法,Mock 外部依赖 | 70% |
| 集成测试 (IT) | 测试多个组件协作,真实数据库 | 20% |
| 端到端测试 (E2E) | 测试完整业务流程(常用于核心业务的流程测试,不会每个接口都写E2E) | 10% |
4. 常用断言
// 普通断言
assertEquals(expected, actual);
assertNotNull(result);
assertTrue(list.isEmpty());
assertThat(actual).isBetween(1, 10);
// 异常断言
assertThrows(BusinessException.class, () -> service.doSomething());
// JSON 断言
assertThatJson(response)
.hasFieldOrProperty("id")
.extracting("name").isEqualTo("test");
// 集合断言
assertThat(users)
.hasSize(3)
.extracting(User::getName)
.containsExactly("A", "B", "C");
5. 测试金字塔
┌─────────┐
│ E2E │ 少量、关键路径
├─────────┤
│ 集成测试 │ 中等、按模块
├─────────┤
│ 单元测试 │ 大量、快速
└─────────┘
6. 注意事项
- 测试独立:每个测试用例独立,不依赖其他测试
- 命名清晰:
方法名_场景_预期命名规范 - Given-When-Then: Arrange-Act-Assert 模式
- 覆盖率适度:核心业务 > 70%,工具类 > 80%
- 避免测试私有方法:通过公开接口测试
最后更新:2026/05/13