@Mock创建依赖的模拟对象,@InjectMocks将这些模拟对象按类型或字段名自动注入被测类:优先尝试构造函数注入,其次setter,最后字段赋值;若匹配失败则字段为null,易引发NPE。

在 Java 的单元测试中,@Mock 和 @InjectMocks 是 Mockito 框架提供的核心注解,用于快速创建模拟对象并自动完成依赖注入,避免手动 new 对象或调用 setter。关键在于理解它们的职责和配合逻辑:@Mock 创建模拟对象,@InjectMocks 尝试把所有 @Mock(以及 @Spy)对象按类型或名称“塞进”被测类的字段中。
什么时候用 @Mock?
@Mock 用来为**依赖对象**创建模拟实例,比如 Service 依赖的 Dao、外部 HTTP 客户端、工具类等。它不执行真实逻辑,所有方法默认返回空值(null、0、false 等),可后续用 when(...).thenReturn(...) 自定义行为。
- 标注在测试类的字段上,通常配合
@ExtendWith(MockitoExtension.class)(JUnit 5)或@RunWith(MockitoJUnitRunner.class)(JUnit 4)使用 - 每个 @Mock 字段对应一个独立的模拟对象,多次使用不会复用
- 不要对被测类本身加 @Mock;那是干扰测试目标
什么时候用 @InjectMocks?
@InjectMocks 标注在**被测对象(SUT)字段**上,Mockito 会尝试将当前测试类中所有已声明的 @Mock(及 @Spy)对象,通过构造函数、setter 方法或直接字段赋值的方式注入进去。
- 仅在字段是具体类(非接口)且有无参构造器时,字段注入才最可靠;否则优先走 setter 或构造器
- 如果多个 @Mock 类型相同,Mockito 会按字段名匹配(如 service → setService(...) 或 private Service service)
- 如果注入失败(比如类型不唯一、没 setter、final 字段),不会报错,但字段保持 null —— 测试运行时可能 NPE,需留意日志或断言前检查
一个典型例子
假设有一个 OrderService 依赖 UserRepository 和 EmailService:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
class OrderService {
private final UserRepository userRepo;
private EmailService emailService; // 注意:这里是 package-private/setter 注入风格
public OrderService(UserRepository userRepo) {
this.userRepo = userRepo;
}
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
// ...
}
对应测试写法:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private UserRepository userRepo;
@Mock
private EmailService emailService;
@InjectMocks
private OrderService orderService; // 自动注入上面两个 mock
@Test
void shouldSendEmailWhenOrderPlaced() {
when(userRepo.findById(123)).thenReturn(new User("Alice"));
orderService.processOrder(123);
verify(emailService).send(any());
}
}
这里 Mockito 会发现 userRepo 有构造参数匹配,emailService 有同名 setter,因此都能成功注入。
常见陷阱与建议
- 别混用 @MockBean(Spring Boot Test)和 @Mock:前者是 Spring 上下文级替换,后者纯 Mockito 本地模拟,用途不同
- 如果类有多个构造器,确保 @InjectMocks 能选到含 mock 参数的那个;否则显式 new + 手动传参更可控
- 字段是 final 且只靠构造器注入?@InjectMocks 支持(从 Mockito 3.5+ 更稳定),但建议搭配构造器参数命名清晰,或改用 @BeforeEach 中手动构建
- 注入后想验证是否成功?打印
System.out.println(orderService)或调试查看字段值,比猜更高效

















