纯单元测试用@mock+@injectmocks,不启spring容器;集成测试用@mockbean替换容器bean。前者快且隔离强,后者适用于需spring环境的controller等场景。

核心是用 @Mock 或 @MockBean 创建虚拟依赖,再通过 stubbing 设定返回值,让被测代码不触达真实接口。
区分场景:纯单元测试 vs Spring 集成测试
两类测试对“mock外部接口”的处理方式不同,选错会导致注入失败或上下文启动慢:
-
纯 Service 层单元测试(推荐首选):不加载 Spring 容器,用
@Mock+@InjectMocks。适合验证业务逻辑,速度快、隔离强。 -
需 Spring 环境的测试(如 Controller 或含 @Autowired 的复杂 Service):用
@MockBean,它会替换 Spring 容器中同类型的 Bean,适用于@SpringBootTest场景。
纯单元测试:用 @Mock 模拟接口(无 Spring 上下文)
适用于只测 Service 方法,且其依赖(如 Repository、FeignClient 接口)可被构造注入。
- 在测试类上加
@ExtendWith(MockitoExtension.class),自动初始化所有@Mock和@InjectMocks字段; - 用
@Mock声明你要 mock 的接口变量(如OrderFeignService orderFeign); - 用
@InjectMocks标注被测对象(如OrderService orderService),Mockito 会把上面的@Mock实例自动设为它的字段; - 在测试方法里用
when(mockInterface.method(...)).thenReturn(...)定义调用行为; - 避免使用
new OrderService(new MockOrderFeign())手动构造——破坏了自动注入,也难维护。
Spring 环境中 mock 外部接口(如 Dubbo、Feign、RestTemplate)
当接口由 Spring 管理(比如 @FeignClient 或 @DubboReference),必须用 @MockBean 替换容器中的实例:
- 测试类需标注
@SpringBootTest(可加classes = {...}缩小上下文); - 声明
@MockBean private RewardFeignClient rewardFeign;—— Spring 启动时会把它注册为单例 Bean,并覆盖原有实现; - 直接写
when(rewardFeign.grant(...)).thenReturn(true)即可,无需额外初始化; - 注意:
@MockBean不能和@Mock混用在同一字段,否则可能因 Bean 冲突导致 NPE 或行为不可预期。
Stubbing 技巧:让 mock 行为更贴近真实
外部接口常返回复杂对象、抛异常或依赖参数,简单 thenReturn 不够用:
- 按参数返回不同结果:
when(userRepo.findById(eq(1L))).thenReturn(u1); when(userRepo.findById(eq(2L))).thenReturn(u2); - 模拟异常:
when(paymentService.pay(any())).thenThrow(new InsufficientBalanceException()); - 动态计算返回值:
when(calculator.add(anyInt(), anyInt())).thenAnswer(invocation -> invocation.getArgument(0, Integer.class) + invocation.getArgument(1, Integer.class)); - 验证是否调用:
verify(orderFeign, times(1)).cancelOrder(eq("ORD-123"));,确保流程走到了外部调用环节。
不复杂但容易忽略:mock 的前提是接口定义清晰、依赖可注入。如果外部接口是静态方法或 new 出来的硬编码实例,就无法直接 mock——得先重构为可注入依赖,再测试才可持续。











