mcpField
U
unit-test-service-layer

unit-test-service-layer

github.com/giuseppe-trisciuoglio测试与质量3653.8k 次安装7 周前更新
代码仓库

关于 unit-test-service-layer

面向编写 Java 服务层测试的开发者,提供使用 Mockito 隔离测试 @Service 类的实践流程,涵盖依赖模拟、交互验证与异常场景。

主要功能

  1. 01使用 MockitoExtension 配置测试
  2. 02模拟仓库和客户端依赖
  3. 03验证返回值与依赖调用
  4. 04覆盖异常和边缘情况
  5. 05运行测试并检查覆盖率

适用场景

  1. 01独立测试 @Service 业务逻辑
  2. 02验证服务对仓库的调用
  3. 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