核心是隔离被测类与真实外部依赖,关键在于让 mock 实例被被测对象实际使用:需通过构造函数注入、@InjectMocks 正确装配或 @Spy 手动设值,避免 new 实例或依赖未注入导致空指针、Wanted but not invoked 或 NPE。

Java 单元测试中用 Mockito 模拟依赖对象,核心是**隔离被测类与真实外部依赖**,只聚焦自身逻辑。关键不在“创建 mock”,而在“让 mock 真正起作用”——这要求 mock 实例必须被被测对象实际使用。
确保 mock 被正确注入到被测对象中
常见失败原因是:声明了 @Mock,却没让它进到被测类里。比如被测类用构造函数或字段注入依赖,但测试中直接 new ClassA(),那依赖字段仍是 null,调用时要么空指针,要么走真实逻辑,导致 Wanted but not invoked 错误。
- 推荐被测类提供显式构造函数(支持 final 字段 + 构造注入),例如:
public ClassA(Bean1 bean1) { this.bean1 = bean1; } - 测试中主动传入 mock 实例:
ClassA sut = new ClassA(mockBean1); - 若用
@InjectMocks,需确认它能通过构造器、setter 或字段成功注入;Spring 环境下不建议混用@Autowired和@InjectMocks,易冲突
分层依赖要逐级控制,不能只 mock 表面一层
当被测类 A 依赖 B,B 又依赖 C 时,仅 @Mock BeanB 不够——因为 mock 的 B 默认所有字段为 null,其中的 C 并未被注入。调用 B 的真实方法时,内部访问 C 就会 NPE。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 避免对中间层(如 ServiceClass)用
@InjectMocks,它不会递归初始化其依赖 - 改用
@Spy创建 B 的部分模拟实例,并手动注入其依赖:ReflectionTestUtils.setField(serviceClass, "utilsClass", mockUtils) - 或者跳过 B 的真实逻辑,直接
@MockB 并用when(b.method()).thenReturn(...)定义行为,绕过深层调用
用 when-then 定义行为,用 verify 验证交互
mock 对象默认不执行任何逻辑,必须显式告诉它“遇到什么输入,返回什么结果”或“抛出什么异常”。验证则用于确认被测逻辑是否按预期调用了依赖。
立即学习“Java免费学习笔记(深入)”;
- 基础 stub:
when(userRepository.findById(1L)).thenReturn(user) - void 方法处理:
doThrow(new RuntimeException()).when(emailService).send(...) - 验证调用:
verify(userRepository, times(1)).save(any(User.class)) - 捕获参数做断言:
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); verify(logger).info(captor.capture()); assertEquals("expected log", captor.getValue());
选对注解组合,减少隐式陷阱
@Mock、@Spy、@InjectMocks 各有定位,混搭不当反而增加不确定性。
-
@Mock:完全虚拟对象,所有方法返回默认值,适合纯接口/无状态类 -
@Spy:包装真实对象,未 stub 的方法仍执行原逻辑,适合需要保留部分行为的场景(如集合操作) -
@InjectMocks仅用于被测对象本身,且应确保其依赖均已用@Mock或@Spy声明;不要对它再嵌套@InjectMocks - JUnit 5 下统一用
@ExtendWith(MockitoExtension.class),无需手动openMocks(this)

















