关于 unit-test-service-layer
面向编写 Java 服务层测试的开发者,提供使用 Mockito 隔离测试 @Service 类的实践流程,涵盖依赖模拟、交互验证与异常场景。
主要功能
- 01使用 MockitoExtension 配置测试
- 02模拟仓库和客户端依赖
- 03验证返回值与依赖调用
- 04覆盖异常和边缘情况
- 05运行测试并检查覆盖率
适用场景
- 01独立测试 @Service 业务逻辑
- 02验证服务对仓库的调用
- 03测试外部 API 失败处理
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ giuseppe-trisciuoglio/ developer-kit --skill unit-test-service-layer安装后在 Claude Code 中输入/unit-test-service-layer确认已加载。
附带文件references/examples.md
详细介绍
使用 Mockito 对服务层进行单元测试
概述
提供使用 Mockito 对 @Service 类进行单元测试的模式。模拟存储库调用,验证方法调用,测试异常场景,并为外部 API 响应设置桩。无需 Spring 容器或数据库,即可实现快速、隔离的测试。
适用场景
- 测试
@Service类中的业务逻辑 - 模拟存储库和外部客户端依赖
- 验证服务与模拟协作者之间的交互
- 测试服务中的错误处理和边界情况
- 编写快速、隔离的单元测试(无需数据库,也不调用 API)
说明
按照此工作流使用 Mockito 测试服务层,并在关键步骤进行验证:
1. 设置测试类
使用 @ExtendWith(MockitoExtension.class) 启用 Mockito 注解。
2. 使用 @Mock 和 @InjectMocks 声明模拟对象
对依赖项(存储库、客户端)使用 @Mock,对待测服务使用 @InjectMocks。
3. 按 Arrange-Act-Assert 流程测试并验证
Arrange(准备):创建测试数据,并使用 when().thenReturn() 配置模拟对象的返回值。
Act(执行):执行待测服务方法。
Assert(断言):
- 使用 AssertJ 断言验证返回值
- 使用
verify()验证模拟对象的交互 - 验证检查点:运行测试并确认测试通过
4. 测试异常场景
使用 when().thenThrow() 配置模拟对象抛出异常。
验证检查点:验证异常类型和消息
5. 验证覆盖情况
- 运行完整测试套件:
mvn test或gradle test - 检查覆盖率报告:
mvn test jacoco:report - 验证检查点:确认每个服务方法都有对应的测试
示例
基本服务测试模式
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldReturnUserWhenFound() {
// Arrange
User expected = new User(1L, "Alice");
when(userRepository.findById(1L)).thenReturn(Optional.of(expected));
// Act
User result = userService.getUser(1L);
// Assert
assertThat(result.getName()).isEqualTo("Alice");
verify(userRepository).findById(1L);
}
@Test
void shouldThrowWhenUserNotFound() {
// Arrange
when(userRepository.findById(999L)).thenReturn(Optional.empty());
// Act & Assert
assertThatThrownBy(() -> userService.getUser(999L))
.isInstanceOf(UserNotFoundException.class);
}
}
验证方法调用
@Test
void shouldSendEmailOnUserCreation() {
User newUser = new User(1L, "Alice", "[email protected]");
when(userRepository.save(any(User.class))).thenReturn(newUser);
enrichmentService.registerNewUser("Alice", "[email protected]");
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcomeEmail("[email protected]");
}
有关其他模式(多个依赖项、参数捕获器、异步服务、InOrder 验证),请参阅 references/examples.md。
最佳实践
- 使用
@ExtendWith(MockitoExtension.class)集成 JUnit 5 - 仅模拟待测服务的直接依赖项
- 验证交互,确保协作方式正确
- 每个测试方法只测试一种行为,保持测试聚焦
- 使用含义明确的变量名:
expectedUser、actualUser、captor - 为值对象和 DTO 创建真实实例(不要模拟它们)
限制和警告
- 不要模拟值对象或 DTO;请使用测试数据创建真实实例。
- 避免模拟过多依赖项;如果服务有太多协作者,请考虑重构。
- 测试必须彼此独立;不要依赖执行顺序。
- 谨慎使用
@Spy;部分模拟更难理解和维护。 - 不要直接测试私有方法;应通过公开方法的行为进行测试。
- 参数匹配器(
any()、eq())不能与实际值混用在同一个桩配置中。 - 避免过度验证;只验证对测试场景重要的交互。
参考资料
评论
支持 Markdown