共计 1562 个字符,预计需要花费 4 分钟才能阅读完成。
背景痛点
在 Java 开发中,手动编写测试用例往往面临几个问题:

- 耗时费力:尤其是复杂业务逻辑,需要大量重复代码来模拟各种场景
- 覆盖率不足:开发者容易忽略边界条件和异常情况
- 维护成本高:随着代码变更,测试用例需要频繁同步更新
技术选型
- Mockito:轻量级 mock 框架,适合大多数场景的对象模拟
- JUnit 5:提供参数化测试、生命周期管理等核心功能
- PowerMock:用于需要 mock 静态方法、构造函数等特殊场景
核心实现
1. 对象 Mock 的创建与配置
// 使用 Mockito 创建 mock 对象
UserService userService = Mockito.mock(UserService.class);
// 配置 mock 行为
Mockito.when(userService.getUserById(1L))
.thenReturn(new User(1L, "testUser"));
2. 异常测试场景的模拟
@Test(expected = UserNotFoundException.class)
public void testGetUserNotFound() {Mockito.when(userService.getUserById(999L))
.thenThrow(new UserNotFoundException());
userService.getUserById(999L);
}
3. 参数化测试的实现
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
public void testIsPositiveNumber(int number) {assertTrue(number > 0);
}
完整代码示例
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class UserServiceTest {
@Test
void testGetUserById() {
// Arrange
UserRepository repository = mock(UserRepository.class);
when(repository.findById(1L)).thenReturn(new User(1L, "admin"));
UserService service = new UserService(repository);
// Act
User result = service.getUserById(1L);
// Assert
assertEquals(1L, result.getId());
assertEquals("admin", result.getUsername());
}
}
性能考量
- 使用
@Execution(ExecutionMode.CONCURRENT)实现测试并行运行 - 避免在测试中创建大量重量级资源
- Mock 对象应尽量轻量化
避坑指南
- 过度 mock:只 mock 必要的依赖,避免测试失真
- 断言过于笼统:应该验证具体行为而非简单通过
- 忽略清理 :记得使用
@AfterEach清理测试数据 - 硬编码测试数据:使用参数化或随机生成测试数据
进阶建议
- 集成到 CI/CD 流水线,设置代码覆盖率阈值
- 使用 SonarQube 等工具分析测试质量
- 定期 review 测试用例的有效性
思考题
如何评估测试用例的生成质量?可以考虑以下几个维度:
- 代码覆盖率(行、分支、方法)
- 边界条件覆盖情况
- 测试执行时间
- 测试失败时的错误信息是否清晰
正文完
