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

使用Mockito @Spy无法模拟私有变量的问题

解决Mockito @Spy模拟私有实例变量时的NullPointerException问题

你的测试代码抛出空指针异常,主要有三个核心问题,咱们一个个拆解:

问题1:@InjectMocks 和手动 new 实例冲突

你在@Before方法里手动new MyObject(),这会覆盖Mockito通过@InjectMocks自动创建并准备注入依赖的实例,导致listOfStrings根本没被注入进去。

问题2:@Spy 注解的列表未初始化

List是接口,Mockito无法直接为接口创建Spy实例,你需要显式指定一个具体实现(比如ArrayList)来初始化这个Spy。

问题3:目标类无依赖注入途径

MyObject里的listOfStrings是私有变量,既没有构造器传入,也没有setter方法,@InjectMocks默认无法完成注入(它优先用构造器,其次是setter,最后才是反射字段注入,但如果前面的步骤被手动new打断,就失效了)。


解决方案1:修改目标类,添加构造器(推荐)

这是最规范的方式,让类的依赖更清晰,也方便Mockito注入:

修正后的目标类 MyObject

public class MyObject {
    private List<String> listOfStrings;

    // 添加构造器,用于注入依赖
    public MyObject(List<String> listOfStrings) {
        this.listOfStrings = listOfStrings;
    }

    public void callListOfStrings() {
        listOfStrings.stream().forEach(System.out::println);
    }
}

修正后的测试类 MyObjectTest

@RunWith(MockitoJUnitRunner.class)
public class MyObjectTest {
    // 显式初始化Spy的具体实现
    @Spy
    List<String> listOfStrings = new ArrayList<>();

    // Mockito会自动通过构造器注入Spy实例
    @InjectMocks
    MyObject myObject;

    // 移除手动new的代码,让@InjectMocks工作
    @Before
    public void before() {
        // 这里不需要再new MyObject了
    }

    @Test
    public void testCallListOfStrings() {
        // 模拟stream方法的返回值
        Mockito.doReturn(new ArrayList<String>().stream()).when(listOfStrings).stream();
        myObject.callListOfStrings();
    }
}

解决方案2:不修改目标类,用反射注入Spy实例

如果不能修改MyObject的代码,咱们可以通过反射把Spy实例手动设置到私有变量里:

修正后的测试类

@RunWith(MockitoJUnitRunner.class)
public class MyObjectTest {
    @Spy
    List<String> listOfStrings = new ArrayList<>();

    MyObject myObject;

    @Before
    public void before() {
        myObject = new MyObject();
        // 用反射注入私有变量
        try {
            Field field = MyObject.class.getDeclaredField("listOfStrings");
            field.setAccessible(true);
            field.set(myObject, listOfStrings);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testCallListOfStrings() {
        Mockito.doReturn(new ArrayList<String>().stream()).when(listOfStrings).stream();
        myObject.callListOfStrings();
    }
}

这样就能让Spy的listOfStrings成功替换掉MyObject里的私有变量,避免空指针异常了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:02:07