mockito模拟外部接口的核心是用虚拟对象替代真实依赖以聚焦业务逻辑验证;纯单元测试用@mock+@injectmocks,spring集成测试则用@mockbean替换容器bean,需按场景选型并避免常见误用。

Mockito 模拟外部接口的核心是不调用真实实现,而是用可控的虚拟对象替代依赖,让测试只聚焦业务逻辑。关键不在“怎么写”,而在于选对方式、分清场景——纯单元测试和 Spring 集成测试的处理逻辑完全不同。
纯单元测试:用 @Mock + @InjectMocks(推荐多数 Service 场景)
适用于只验证 Service 内部逻辑,且其依赖(如 FeignClient、DAO 接口)能通过构造器或 setter 注入的情况。不启动 Spring 容器,速度快、隔离性强。
- 在测试类上加 @ExtendWith(MockitoExtension.class),自动初始化所有注解字段
- 用 @Mock 声明要模拟的接口变量,比如
@Mock private UserRepo userRepo; - 用 @InjectMocks 标注被测对象,比如
@InjectMocks private OrderService orderService;,Mockito 会把上面的 mock 实例自动注入进去 - 在测试方法中用
when(userRepo.getUserName(1L)).thenReturn("Alice")定义行为,支持按参数返回不同结果、抛异常、动态计算等
Spring 环境测试:用 @MockBean(Controller 或含 @Autowired 的复杂 Service)
当被测类依赖由 Spring 容器管理的 Bean(如 @FeignClient、@DubboReference、@Mapper),必须用 @MockBean 替换容器中的实例,否则注入失败或上下文加载慢。
- 测试类标注 @SpringBootTest(可加
classes = {OrderService.class}缩小上下文) - 声明
@MockBean private RewardFeignClient rewardFeign;,Spring 启动时自动注册并覆盖原 Bean - 直接写
when(rewardFeign.grant()).thenReturn(true)即可,无需手动初始化 - 注意:@MockBean 和 @Mock 不要混用于同一字段,否则可能引发 NPE 或行为错乱
Stubbing 行为要贴近真实场景
简单 thenReturn 往往不够。外部接口常有参数敏感、异常路径、对象嵌套等特性,需针对性 stub:
- 按参数区分响应:
when(userRepo.findById(eq(1L))).thenReturn(u1); when(userRepo.findById(eq(2L))).thenReturn(u2); - 模拟异常:
when(paymentService.pay(any())).thenThrow(new InsufficientBalanceException()); - 动态返回(如自增 ID):
when(idGenerator.next()).thenAnswer(i -> counter++); - 验证是否被调用及参数:
verify(userRepo, times(1)).deleteUser(1L);
常见踩坑提醒
不是所有依赖都适合 mock,也不是所有 mock 都能“一劳永逸”:
- 避免手动 new 被测对象再传 mock 实例(如
new OrderService(new MockUserRepo())),破坏了自动注入,也难维护 - 静态方法、final 类、private 方法默认不能 mock,需引入
mockito-inline并启用 ByteBuddy - 不要 mock 值对象(如 DTO、VO)、工具类(如 StringUtils)、或被测类自身——mock 的是协作对象,不是被测对象
- 过度 stub 会让测试失去意义,重点应是“业务逻辑分支是否走对”,而不是“每个 mock 都写满”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











