
当被测类构造函数依赖注入对象的返回值时,@InjectMocks 会早于 @BeforeEach 执行,导致无法提前 stub 依赖方法;此时应放弃 @InjectMocks,改用手动实例化并传入已配置好的 mock 对象。
当被测类构造函数依赖注入对象的返回值时,`@injectmocks` 会早于 `@beforeeach` 执行,导致无法提前 stub 依赖方法;此时应放弃 `@injectmocks`,改用手动实例化并传入已配置好的 mock 对象。
在使用 Mockito 进行单元测试时,@Mock 和 @InjectMocks 是常用组合,但它们存在一个关键执行顺序限制:@InjectMocks 的字段注入发生在测试生命周期的初始化阶段(早于 @BeforeEach)。这意味着,若被测类(如 A)在构造函数中直接调用被 mock 对象(如 B)的方法(例如 b.getC()),而该方法尚未 stub,就会返回 null,进而触发 requireNonNull 抛出 NullPointerException,导致测试甚至无法启动。
上述问题的根本原因在于:
-
@InjectMocks不支持“延迟注入”或“按需注入”; -
@Mock字段虽已创建,但其行为(stub)必须在注入前完成,而标准生命周期中无此钩子。
✅ 推荐解决方案:弃用 @InjectMocks,手动构造被测对象
这种方式更可控、更显式,也更符合测试可读性与可维护性原则:
@ExtendWith(MockitoExtension.class)
class ATest {
@Mock
B b;
A a; // 不加 @InjectMocks
@BeforeEach
void setUp() {
when(b.getC()).thenReturn("mocked-value"); // ✅ 先 stub
a = new A(b); // ✅ 再手动传入,确保构造逻辑使用已配置的 mock
}
@Test
void test() {
assertNotNull(a.c);
assertEquals("mocked-value", a.c);
}
}? 注意事项与最佳实践:
- 避免过度依赖
@InjectMocks,尤其当构造逻辑复杂、含非空校验、或依赖方法调用时; - 手动构造不仅规避了生命周期冲突,还使测试意图一目了然——谁被注入、何时注入、行为如何定义;
- 若类有多个依赖,仍可统一在
@BeforeEach中完成所有 stub + 构造,保持一致性; - 如需复用构造逻辑,可封装为私有工厂方法(如
createTestInstance()),提升可读性。
总之,在构造即求值(eager evaluation)场景下,显式优于隐式。放弃 @InjectMocks 不是退步,而是对测试健壮性与可调试性的主动增强。

















