spring boot单元测试关键在于验证业务逻辑而非容器注入,主流分两类:轻量级纯单元测试(@mockitotest+@injectmocks)和集成式测试(@springboottest+@mockbean),前者快且独立,后者适用于需spring上下文的场景。

Spring Boot 中对注入的 Bean 做单元测试,关键不是“测 Spring 容器是否注入成功”,而是验证该 Bean 的业务逻辑是否按预期工作——同时隔离它所依赖的其他 Bean。主流做法分两类:轻量级纯单元测试(不启动 Spring 上下文)和带上下文的集成式测试(启动最小 Spring 环境)。选哪种,取决于你测的是什么。
优先用 @MockitoTest + @InjectMocks 测 Service 层逻辑
这是真正意义上的单元测试:快、独立、只聚焦被测类自身行为。适用于 Service、Util、DTO 转换等无 Spring 特性依赖的组件。
- 不加 @SpringBootTest,完全绕过 Spring 容器启动,毫秒级执行
- 用 @Mock 声明依赖对象(如 UserRepository、RedisTemplate)
- 用 @InjectMocks 创建被测对象,并自动把 @Mock 注入进去
- 手动调用方法、断言返回值、verify 交互次数
示例:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserServiceImpl userService;
@Test
void shouldSendWelcomeEmailWhenUserCreated() {
when(userRepository.save(any())).thenReturn(new User(1L, "Alice"));
userService.createUser("Alice", "a@example.com");
verify(emailService).sendWelcomeEmail("a@example.com");
}
用 @SpringBootTest + @MockBean 测需要 Spring 上下文的场景
当你必须测 AOP 切面、@Transactional、@Async、@Scheduled、@EventListener 或自动装配逻辑时,就得启动 Spring 环境。此时用 @MockBean 替换真实 Bean,避免连数据库或调外部服务。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- @MockBean 会替换 Spring 容器中同类型的 Bean,所有未 stub 的方法默认返回 null 或基本类型默认值
- 配合 @Autowired 获取被测 Bean(通常是 Controller 或加了注解的 Service)
- 适合验证 Web 层入口、事务边界、配置生效情况等
示例:
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) // 不动真实库
class UserControllerTest {
@Autowired
private UserController userController;
@MockBean
private UserService userService;
@Test
void shouldReturnUserById() {
when(userService.findById(1L)).thenReturn(new User(1L, "Bob"));
ResponseEntity
assertThat(result.getBody().getName()).isEqualTo("Bob");
}
@SpyBean 用于部分打桩 + 真实调用混合场景
当你要保留某个 Bean 的大部分真实行为(比如真实查库),但只想拦截其中一两个方法做定制返回(比如 mock 某个耗时的远程调用),就用 @SpyBean。
- 它包装真实 Bean,未 stub 的方法仍走原实现
- stub 时必须用 doReturn(...).when(spy).method(),不能用 when().thenReturn()(否则可能触发真实方法)
- 常见于测试 Repository 或 FeignClient 的部分降级逻辑
示例:
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@SpyBean
private PaymentClient paymentClient;
@Test
void shouldProcessOrderEvenIfPaymentFails() {
doThrow(new RuntimeException("timeout")).when(paymentClient).charge(any());
OrderResult result = orderService.placeOrder(new Order());
assertThat(result.getStatus()).isEqualTo("PENDING_PAYMENT");
}
避坑要点
- 别在单元测试里写 @SpringBootTest + @Transactional —— 这是集成测试套路,启动慢、易受环境干扰
- @Mock 和 @MockBean 不要混用:前者是 Mockito 原生,后者是 Spring Boot 提供的容器级模拟
- Controller 层测试优先用 @WebMvcTest,它只加载 Web 相关 Bean,比 @SpringBootTest 更轻量
- 确保测试类路径下没有意外激活的 profile(如 test.yml 写错导致连真实 Redis)










