You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mockito:使用@Spy与@InjectMocks时如何注入构造方法的@Mock修饰final字段

解决Mockito中@Spy结合构造方法注入final@Mock字段的问题

首先,咱们先拆解下你代码里的核心问题:你在声明service时直接手动调用构造方法new ServiceTest(usedAsNonFinal, usedAsFinal),但这时候@Mock修饰的usedAsNonFinal和usedAsFinal还没被Mockito初始化(Mockito的Mock对象是由MockitoJUnitRunner在测试类初始化后期才创建的),所以你传入的其实是null;而且因为usedAsFinal是final字段,一旦通过构造方法赋值后就无法再被修改,后续@InjectMocks也没法替换它。

下面给你两种可行的解决方案:

方案一:让Mockito自动处理构造方法注入(推荐)

去掉手动new的代码,让@Spy和@InjectMocks配合自动完成依赖注入。Mockito的注入优先级是构造方法 > Setter方法 > 字段注入,对于final字段来说,构造方法注入是最贴合Java语法的方式。

修改后的代码如下:

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
    @Mock
    private SomeObject usedAsNonFinal;
    @Mock
    private SomeObject usedAsFinal;

    // 去掉手动new,Mockito会自动匹配构造方法参数注入Mock对象
    @Spy
    @InjectMocks
    private Service service;

    @Before
    public void setUp() {
        // 这里可以添加Spy的额外初始化逻辑(如果需要)
        // MockitoAnnotations.initMocks(this);  // 可选,因为RunWith已经处理了Mock初始化
    }

    // 你的测试方法...
}

Mockito会自动找到Service类的构造方法,根据参数名称(或类型,如果只有一个构造方法的话)匹配@Mock修饰的对象,完成注入。final字段会通过构造方法被正确赋值为Mock对象,完全符合final的特性。

方案二:在@Before方法中手动创建Spy

如果你确实需要手动控制Service的实例化时机,可以把Spy的创建逻辑放到@Before方法里。因为MockitoJUnitRunner会在@Before方法执行前完成所有@Mock对象的初始化,这时候传入的Mock对象已经是有效的实例了。

代码示例:

@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
    @Mock
    private SomeObject usedAsNonFinal;
    @Mock
    private SomeObject usedAsFinal;

    private Service service;

    @Before
    public void setUp() {
        // 此时@Mock对象已经被初始化,传入构造方法创建真实对象后再包装为Spy
        service = Mockito.spy(new Service(usedAsNonFinal, usedAsFinal));
        // 如果还有其他非final字段需要注入,可以在这里手动调用Setter方法
    }

    // 你的测试方法...
}

这种方式能确保final字段被正确赋值为Mock对象,同时保留Spy的特性——既可以调用真实方法,也可以按需mock部分方法的行为。

额外注意点

  • 如果你用的是Mockito 2+版本,确保你的测试类和被测类都符合Mockito的要求(比如不是final类,方法不是private等,不过Spy支持真实对象,所以只要真实对象能被实例化即可)。
  • 避免在字段声明阶段直接new被测对象,因为此时Mock对象还未初始化,容易导致空指针或注入失败。

内容的提问来源于stack exchange,提问作者RichardK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:03:59