@mock 创建模拟对象,@injectmocks 创建被测类实例并注入这些模拟对象;前者生成默认返回值的空壳依赖,后者通过反射将 mock 按构造函数、setter 或字段名规则注入被测对象。

@Mock 创建模拟对象,@InjectMocks 创建被测类实例并注入这些模拟对象。 它们不是替代关系,而是协作关系:一个负责“造假依赖”,一个负责“组装被测对象”。
① @Mock:只造“假人”,不干活
@Mock 标记的字段,会生成一个空壳对象——方法调用默认不执行真实逻辑,返回默认值(null、0、false 等)。你必须用 when(...).thenReturn(...) 显式定义它在什么输入下返回什么结果。
- 它不关联任何真实类实现,也不调用构造函数
- 适合模拟外部不可控依赖:数据库 DAO、HTTP 客户端、消息发送器等
- 多个同类型 mock 时,Mockito 优先按字段名匹配(如
@Mock UserService userService会注入到名为userService的字段)
② @InjectMocks:组装“主角”,把假人塞进去
@InjectMocks 标记的字段,是你要真正测试的那个类的实例。Mockito 会尝试将当前测试类中所有 @Mock(或 @Spy)对象,按规则“塞进”这个实例里——不是 Spring 那种容器注入,而是纯反射操作。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 注入顺序:先找匹配参数的构造函数 → 再找 setter 方法 → 最后直接赋值给同名/同类型的字段
- 它不启动 Spring 容器,也不处理 AOP、@PostConstruct、循环依赖等
- 如果注入失败(比如字段名不匹配、类型模糊),该字段可能为 null,运行时抛 NPE
③ 典型错误场景
常见问题不是注解写错,而是没理解它们的边界:
- 写了
@InjectMocks private Service service;,但忘了配@Mock Dao dao;→ service 里 dao 字段仍是 null - Service 有两个构造函数,其中一个带
Dao参数,另一个不带 → Mockito 选了无参构造,dao 没注入进去 - 用
@Mock模拟了一个接口,但被测类字段类型是其实现类 → 类型不匹配,注入失败 - 在 Spring Boot 测试里混用
@MockBean和@InjectMocks→ 前者走 Spring 上下文,后者绕过容器,行为冲突
④ 一个最小可运行例子
假设要测 OrderService,它依赖 PaymentClient 和 InventoryRepo:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock PaymentClient paymentClient;
@Mock InventoryRepo inventoryRepo;
@InjectMocks OrderService orderService; // 自动把上面两个 mock 塞进去
@Test
void shouldRejectWhenInventoryInsufficient() {
when(inventoryRepo.checkStock("A123")).thenReturn(false);
boolean result = orderService.placeOrder("A123");
assertFalse(result);
verify(paymentClient, never()).charge(any());
}
}
这里 orderService 是真实对象,它的业务逻辑全都会执行;而 paymentClient 和 inventoryRepo 是假的,行为由你完全控制。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










