高质量单元测试的核心是精准隔离、聚焦逻辑、验证行为,而非盲目mock所有方法;必须mock有IO/网络副作用的外部依赖,不该mockJDK类型和领域实体,谨慎mock工具类,并用@Mock/@InjectMocks简化setup、when/verify验证行为与交互。

用 Mockito 写出高质量的单元测试,核心不是“把所有方法都 mock 掉”,而是精准隔离、聚焦逻辑、验证行为。关键在于理解被测代码真正依赖什么,只模拟那些影响测试稳定性和可读性的外部协作对象。
明确哪些该 mock,哪些不该 mock
Mockito 的价值在于解耦,但滥用反而降低可维护性。记住几个实用边界:
-
必须 mock 的:数据库访问层(如
UserRepository)、HTTP 客户端(如RestTemplate)、消息队列生产者、支付网关等——它们有 IO、网络、状态副作用,无法在测试中可靠运行 -
不该 mock 的:
String、List、LocalDateTime、Optional等 JDK 类型;领域实体(如User、Order)——直接 new 或用 Builder 构造更清晰、更安全 -
谨慎 mock 的:工具类(如
StringUtils、JsonUtil)——优先调用真实实现;若逻辑复杂且需控制返回,可考虑封装成服务再 mock
用注解简化 setup,避免样板代码
比起手动调用 Mockito.mock(),推荐使用 @Mock + @InjectMocks 组合,配合 @BeforeEach 初始化:
@ExtendWith(MockitoExtension.class) // JUnit 5 推荐方式,替代 openMocks()
class OrderServiceTest {
@Mock
private PaymentGateway paymentGateway;
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderService orderService; // 自动注入所有 @Mock 字段
@Test
void shouldPlaceOrderAndSaveWhenPaymentSuccess() {
// 给定
when(paymentGateway.process(99.9)).thenReturn(true);
when(orderRepository.save(any(Order.class))).thenAnswer(invocation ->
((Order) invocation.getArgument(0)).setId(1001L)
);
// 当
boolean result = orderService.placeOrder(new Order("iPhone", 99.9));
// 那么
assertTrue(result);
verify(orderRepository).save(argThat(order -> "iPhone".equals(order.getItem())));
verify(paymentGateway).process(99.9);
}
}
注意:@ExtendWith(MockitoExtension.class) 是 JUnit 5 的标准做法,比手动 openMocks(this) 更简洁、更安全。
立即学习“Java免费学习笔记(深入)”;
用 when().thenReturn() 控制返回,用 verify() 验证交互
高质量测试关注两件事:结果是否正确(断言返回值),以及协作是否合规(验证调用)。
-
设定行为:用
when(...).thenReturn(...)模拟正常路径;用when(...).thenThrow(...)覆盖异常分支 -
验证行为:用
verify(mock, times(1)).method(...)确保关键依赖被调用;用argThat(...)或eq(...)校验参数内容,而非仅检查调用次数 -
避免过度 stubbing:只 stub 测试用到的方法。mock 对象上未 stub 的方法默认返回 null/0/false/empty,这是宽松模式的优势,别为“安全”而写一堆无意义的
when(xxx).thenReturn(...)
测试要覆盖“行为契约”,不只是 happy path
一个健壮的测试套件应包含三类典型场景:
- 成功流程:依赖返回预期值,主逻辑正常流转
- 失败流程:依赖抛异常或返回 false,验证业务是否正确降级或回滚(例如:支付失败时订单不保存)
-
边界与空值:输入 null、空集合、非法参数,验证防御性逻辑是否生效(如
Objects.requireNonNull()或自定义校验)
比如验证支付失败场景:
@Test
void shouldNotSaveOrderWhenPaymentFails() {
when(paymentGateway.process(99.9)).thenReturn(false);
boolean result = orderService.placeOrder(new Order("iPad", 99.9));
assertFalse(result);
verify(orderRepository, never()).save(any()); // 关键:确保没存库
}
不复杂但容易忽略:高质量不是靠行数堆出来的,而是靠每次 mock 都有明确目的、每次 verify 都对应一条业务规则。写完一个测试,能让人一眼看出“它在保护哪条业务逻辑”,这才是 Mockito 的真正用法。


















