单元测试
单元测试是保证代码质量的基础。
📋 JUnit 测试框架
单元测试示例:
java
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserRepository userRepository;
@Test
void testGetUserById() {
// 准备测试数据
User user = new User();
user.setId(1L);
user.setName("John");
// Mock行为
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
// 执行测试
User result = userService.getUserById(1L);
// 验证结果
assertNotNull(result);
assertEquals("John", result.getName());
}
}🎯 Mock 测试
Mock测试用于模拟外部依赖,隔离测试单元。
1. Mockito 使用示例
Mockito 基础用法:
java
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testGetUserById() {
// 准备测试数据
User user = new User();
user.setId(1L);
user.setName("John");
user.setEmail("john@example.com");
// Mock行为
when(userRepository.findById(1L))
.thenReturn(Optional.of(user));
// 执行测试
User result = userService.getUserById(1L);
// 验证结果
assertNotNull(result);
assertEquals("John", result.getName());
assertEquals("john@example.com", result.getEmail());
// 验证方法调用
verify(userRepository, times(1)).findById(1L);
}
@Test
void testGetUserByIdNotFound() {
// Mock返回空
when(userRepository.findById(999L))
.thenReturn(Optional.empty());
// 验证抛出异常
assertThrows(UserNotFoundException.class, () -> {
userService.getUserById(999L);
});
}
}2. 测试覆盖率
代码覆盖率指标:
- 行覆盖率(Line Coverage):执行的代码行数比例
- 分支覆盖率(Branch Coverage):执行的分支路径比例
- 方法覆盖率(Method Coverage):执行的方法比例
- 类覆盖率(Class Coverage):执行的类比例
覆盖率目标:
| 覆盖率类型 | 目标 | 说明 |
|---|---|---|
| 行覆盖率 | > 80% | 核心业务逻辑应该达到更高 |
| 分支覆盖率 | > 70% | 确保所有分支路径都被测试 |
| 方法覆盖率 | > 90% | 所有公共方法都应该有测试 |
3. 测试最佳实践
测试原则:
- FIRST原则:Fast(快速)、Independent(独立)、Repeatable(可重复)、Self-Validating(自验证)、Timely(及时)
- AAA模式:Arrange(准备)、Act(执行)、Assert(断言)
- 测试隔离:每个测试应该独立,不依赖其他测试
测试示例:
java
@Test
void testCreateUser() {
// Arrange(准备)
CreateUserRequest request = new CreateUserRequest();
request.setName("John");
request.setEmail("john@example.com");
User savedUser = new User();
savedUser.setId(1L);
savedUser.setName("John");
savedUser.setEmail("john@example.com");
when(userRepository.save(any(User.class)))
.thenReturn(savedUser);
// Act(执行)
User result = userService.createUser(request);
// Assert(断言)
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals("John", result.getName());
verify(userRepository, times(1)).save(any(User.class));
}📋 测试框架对比
| 框架 | 特点 | 适用场景 |
|---|---|---|
| JUnit | 标准Java测试框架 | Java项目 |
| TestNG | 功能更强大 | 复杂测试场景 |
| Mockito | Mock框架 | 依赖隔离 |
| PowerMock | 支持静态方法Mock | 遗留代码测试 |
| AssertJ | 流畅断言 | 更易读的断言 |
参考资源:
- JUnit 官方文档
- Mockito 官方文档
- 视频推荐:JUnit 单元测试实战
- 书籍推荐:《JUnit实战》、《单元测试的艺术》
📋 测试策略
1. 测试金字塔
2. 测试用例设计
测试用例设计方法:
- 等价类划分:将输入分为有效和无效等价类
- 边界值分析:测试边界值和边界附近的值
- 错误推测:根据经验推测可能的错误场景
3. 测试数据管理
测试数据策略:
- 测试夹具(Fixture):准备标准化的测试数据
- 数据生成:使用工具生成测试数据
- 数据隔离:每个测试使用独立的数据,避免相互影响
📖 学习资源
视频教程
书籍推荐
- 《JUnit实战》 - Petar Tahchiev
- 《单元测试的艺术》 - Roy Osherove
- 《测试驱动开发:实战与模式解析》 - 肯特·贝克
- 《有效单元测试》 - Lasse Koskela
网站资源
工具推荐
- 测试框架:JUnit、TestNG、pytest
- Mock框架:Mockito、PowerMock、EasyMock
- 覆盖率工具:JaCoCo、Cobertura、Clover
- 测试报告:Allure、Surefire、Gradle Test Report
✅ 单元测试检查清单
- [ ] 单元测试覆盖率达标(>80%)
- [ ] 核心业务逻辑有完整测试
- [ ] 边界条件和异常场景有测试
- [ ] 使用了Mock隔离外部依赖
- [ ] 测试用例遵循FIRST原则
- [ ] 测试数据管理规范
- [ ] 测试执行快速可靠