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

JUnit5+Mockito+SpringBoot中如何给@Spy对象注入@Mock依赖

解决Mockito Spy对象依赖注入NullPointerException问题

你的问题核心是手动初始化Spy对象时,传入的@Mock实例还未被Mockito完成初始化,同时在同一个字段上混用@Spy和@InjectMocks会干扰Mockito的注入逻辑,导致MyTargetHelper内部的databaseService为null。

以下是两种可行的解决方案:

方案一:让Mockito自动管理Spy的创建与注入

去掉MyTargetHelper的手动实例化,让Mockito通过注解完成Spy创建和依赖注入:

@ExtendWith(MockitoExtension.class)
class TargetTest {

    @InjectMocks
    Target target;

    @Mock
    DatabaseService databaseService;

    @Mock
    AlertService alertService;

    @Spy
    Map<SomeEnum, Double> someMap = new EnumMap<>(SomeEnum.class);

    // 移除手动new,由Mockito创建Spy并注入依赖
    @Spy
    @InjectMocks
    MyTargetHelper inputHelper;

    static final List<InputPojo> inputs;

    static {
        // 保留inputs和someMap的初始化逻辑
        someMap.put(SomeEnum.EXAMPLE, 0.5);
        inputs = List.of(new InputPojo("id1"), new InputPojo("id2"));
    }

    @Test
    @SneakyThrows
    void testTarget() {
        when(databaseService.fetchForId(anyString())).thenReturn("fetch success");

        target.testMethod(inputs);
        
        // 验证Helper方法调用(可选)
        verify(inputHelper, times(1)).process(inputs);
    }
}

方案二:手动创建实例后转为Spy(更可控)

如果需要更精准地控制实例创建流程,可以先初始化所有依赖,再将真实实例包装为Spy,最后手动注入到Target:

@ExtendWith(MockitoExtension.class)
class TargetTest {

    Target target;

    @Mock
    DatabaseService databaseService;

    @Mock
    AlertService alertService;

    @Spy
    Map<SomeEnum, Double> someMap = new EnumMap<>(SomeEnum.class);

    MyTargetHelper inputHelper;

    static final List<InputPojo> inputs;

    static {
        // 初始化inputs和someMap
        someMap.put(SomeEnum.EXAMPLE, 0.5);
        inputs = List.of(new InputPojo("id1"), new InputPojo("id2"));
    }

    @BeforeEach
    void setUp() {
        // 创建真实Helper实例,传入已准备好的依赖
        MyTargetHelper realHelper = new MyTargetHelper(someMap, alertService, databaseService);
        // 包装为Spy,保留真实逻辑同时支持验证/ stub
        inputHelper = Mockito.spy(realHelper);
        // 手动注入到Target
        target = new Target(inputHelper);
    }

    @Test
    @SneakyThrows
    void testTarget() {
        when(databaseService.fetchForId(anyString())).thenReturn("fetch success");

        target.testMethod(inputs);
    }
}

关键注意事项

  • 避免在@Spy字段上手动new对象:Mockito的@Mock对象初始化晚于测试类实例化,手动传入的是未初始化的null,导致依赖注入失败。
  • @Spy与@InjectMocks配合时,Mockito会自动将@Mock/@Spy依赖注入到Spy对象中,无需手动实例化。
  • 使用Spy的核心是保留目标类的真实业务逻辑,上述方案均能保证Helper的真实process方法被执行,同时Mock的DatabaseService和AlertService能正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 20:38:10