使用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
相关产品推荐
相关产品推荐

