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

JUnit+Mockito测试中@Spy调用真实方法时依赖Utility类Mock失效引发NPE的问题求助

JUnit+Mockito测试中@Spy调用真实方法时依赖Utility类Mock失效引发NPE的问题求助

我来帮你分析下问题根源,再给你两个实用的解决方案:

问题本质

你用@Spy创建了SupportClass的代理对象,但这个Spy是基于真实对象生成的——而真实SupportClass里的utility字段靠Spring的@Autowired注入,测试环境下Spring容器没启动,所以这个字段默认是null。你虽然用@Mock创建了UtilityClass的Mock实例,但没把这个Mock注入到Spy的SupportClass对象里,导致调用populateMap时触发空指针异常。

另外你提到因为SupportClass还有其他被Stub的方法,没法用@InjectMocks——这个没问题,我们可以手动完成注入。


方案一:手动将Mock的UtilityClass注入到Spy对象中

你可以用Spring的ReflectionTestUtils(或者给SupportClass加个setter方法),把Mock的UtilityClass塞到Spy实例里,这样调用真实方法时就会用到Mock的Utility,不会出现NPE了:

@ExtendWith(MockitoExtension.class)
public class MyClassTest {
    @Spy
    private SupportClass supportClass;

    @Mock
    private UtilityClass utilityClass;

    @BeforeEach
    void setUp() {
        // 用ReflectionTestUtils反射注入Mock(不需要setter方法)
        ReflectionTestUtils.setField(supportClass, "utility", utilityClass);
        
        // 如果SupportClass有setter方法,也可以直接调用更直观:
        // supportClass.setUtility(utilityClass);
    }

    @Test
    void testMethod() {
        // 先Stub UtilityClass的doSomething方法,避免执行真实逻辑
        doNothing().when(utilityClass).doSomething(anyMap());

        MyClass myClass = new MyClass();
        // 把Spy的supportClass注入到MyClass中(同样可以用反射或setter)
        ReflectionTestUtils.setField(myClass, "supportClass", supportClass);

        myClass.method();

        // 接下来就可以获取被填充的Map做断言了
        // (注意:你可能需要调整MyClass的代码,让mapToBePopulated能被测试获取,比如改成成员变量或返回它)
    }
}

方案二:不调用真实的populateMap方法,直接模拟Map填充行为

如果你其实不需要测试populateMap的真实逻辑,只是想让传入的Map被填充以便后续测试,那完全可以跳过真实方法调用,用doAnswer来StubpopulateMap,直接在Stub逻辑里给Map塞测试数据:

@ExtendWith(MockitoExtension.class)
public class MyClassTest {
    @Mock
    private SupportClass supportClass;

    @Test
    void testMethod() {
        // 用doAnswer模拟populateMap的行为:当方法被调用时,往传入的Map里放数据
        doAnswer(invocation -> {
            // 获取方法参数里的Map
            Map<String, List<Object>> map = invocation.getArgument(0);
            // 手动填充你需要的测试数据
            map.put("key1", Arrays.asList("value1", "value2"));
            map.put("key2", Arrays.asList("value3"));
            return null; // 因为populateMap是void方法,返回null即可
        }).when(supportClass).populateMap(anyMap());

        MyClass myClass = new MyClass();
        ReflectionTestUtils.setField(myClass, "supportClass", supportClass);

        myClass.method();

        // 此时MyClass里的mapToBePopulated已经被模拟填充,可以继续后续断言
    }
}

这个方案的好处是完全绕开了SupportClass的真实逻辑,也不需要处理UtilityClass的依赖,适合你只关心最终Map数据的场景。

备注:内容来源于stack exchange,提问作者Pi53

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 15:49:32