最常见的原因是测试类未添加@extendwith(mockitoextension.class)(junit 5)或@runwith(mockitojunitrunner.class)(junit 4),导致@mock等注解未生效,字段注入失败而为null。

为什么 @BeforeEach 初始化的模拟对象在测试中为 null
最常见的原因是测试类没用 @ExtendWith(MockitoExtension.class) 或没加 @RunWith(MockitoJUnitRunner.class)(JUnit 4)。Mockito 不会自动处理 @Mock、@InjectMocks 这些注解,必须显式启用扩展。如果漏了这一步,@BeforeEach 方法里看似调用了 mock(),但实际字段仍是 null——因为字段注入根本没发生。
实操建议:
- JUnit 5 必须加
@ExtendWith(MockitoExtension.class)在类上 - JUnit 4 用
@RunWith(MockitoJUnitRunner.class),或手动在@BeforeEach里写MockitoAnnotations.openMocks(this) - 别把
@Mock字段声明成final,否则 Mockito 注入失败且不报错
@BeforeEach 中该 new 实例还是用 @Mock/@Spy
取决于你要测的是「被测对象自身逻辑」还是「它与依赖的协作」。如果被测类依赖 UserService,而你只想验证它怎么处理返回值,就该用 @Mock UserService userService;但如果你要测它内部 new 出来的 LocalDate.now() 行为,那得在 @BeforeEach 里用 Mockito.mockStatic(LocalDate.class)(需 Mockito 4.11+)。
实操建议:
-
@Mock用于外部依赖(如 Repository、FeignClient),由@BeforeEach前完成注入 -
@Spy适合部分真实行为 + 部分 stub,比如 spy 一个真实 service,只 stub 其中一个方法 - 避免在
@BeforeEach里反复new复杂对象——既慢又难维护,优先交由 Mockito 管理生命周期
@BeforeEach 和 @BeforeAll 的初始化边界在哪
@BeforeAll 是整个测试类只跑一次,共享状态;@BeforeEach 是每个 @Test 前重置。一旦你在 @BeforeAll 里初始化了可变对象(比如 ArrayList),后续测试会互相污染——A 测试 add 了元素,B 测试一进来就看到脏数据。
实操建议:
- 数据库连接、HTTP 客户端等重型资源放
@BeforeAll,但要用static修饰 - 所有带状态的对象(List、Map、Builder 实例、Mock 对象的行为配置)必须放在
@BeforeEach里重建或 reset - 用
Mockito.reset(mockObj)可以清空 stub 和调用记录,但不如直接重新 mock 干净
Mockito reset 后 verify 失败的典型场景
很多人在 @BeforeEach 末尾写 reset(userService),结果 @Test 里 verify(userService).save(...) 报 Wanted but not invoked。这是因为 reset 清掉了所有 stub 和 verify 记录,相当于“从零开始”,但测试里又没重新 when(...).thenReturn(...),导致方法调用后无响应,verify 自然找不到匹配。
实操建议:
- 除非明确需要跨测试复用同一个 mock 实例,否则不要在
@BeforeEach里reset——直接重新声明@Mock更安全 - 如果真要用
reset,记得在每个测试开头补全when配置 - 验证调用次数时,优先用
verify(mock, times(1)).method()而非verify(mock).method(),避免因 reset 导致的隐式状态干扰
最易被忽略的一点:@BeforeEach 方法本身不能有参数,也不能是 private——JUnit 5 会跳过它而不报错,导致你以为初始化执行了,其实什么都没做。










