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

使用Mockito测试SpringBoot启动类报ApplicationListener无法实例化错误如何解决

问题根因

你的报错本质是单元测试执行时触发了真实的SpringApplication初始化流程,程序加载了classpath下spring.factories文件中定义的ApplicationListener实现类,其中FunctionProxyApplicationListener初始化时依赖的javac工具类在单元测试的类加载环境下类型不匹配,最终抛出实例化失败异常。
你现有测试代码的核心问题是:仅声明了SpringApplicationBuilder的mock实例,但没有拦截主类main方法中new SpringApplicationBuilder()的构造调用,导致真实的SpringApplicationBuilder对象被创建,后续走了完整的Spring启动逻辑,没有达到Mock隔离的效果。

修复方案

修改后的测试类代码如下:

import com.ab.obs.spring.ObsSpringBootApplicationForCloud;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.builder.SpringApplicationBuilder;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ObsDocumentApplication.class, ObsSpringBootApplicationForCloud.class, SpringApplicationBuilder.class})
public class ObsDocumentApplicationTest {
    @Mock
    private SpringApplicationBuilder applicationBuilder;

    @Test
    public void testMainClass() throws Exception {
        // 拦截SpringApplicationBuilder的构造调用,返回mock实例
        whenNew(SpringApplicationBuilder.class).withNoArguments().thenReturn(applicationBuilder);
        // mock链式调用的返回,支持builder流式调用
        when(applicationBuilder.sources(any(Class.class))).thenReturn(applicationBuilder);
        when(applicationBuilder.run(any(String[].class))).thenReturn(null);
        // mock静态工具类的初始化方法
        PowerMockito.doNothing().when(ObsSpringBootApplicationForCloud.class, "initBase", eq(applicationBuilder), eq("csl-svc-obs-test"));

        // 执行测试方法
        ObsDocumentApplication.main(new String[] {});

        // 验证调用流程是否符合预期
        verify(ObsSpringBootApplicationForCloud.class);
        ObsSpringBootApplicationForCloud.initBase(eq(applicationBuilder), eq("csl-svc-obs-test"));
        verify(applicationBuilder).sources(eq(ObsDocumentApplication.class));
        verify(applicationBuilder).run(eq(new String[]{}));
    }

    @Test
    public void testConfigure() {
        ObsDocumentApplication application = new ObsDocumentApplication();
        PowerMockito.doNothing().when(ObsSpringBootApplicationForCloud.class, "initBase", eq(applicationBuilder), eq("csl-svc-obs-test"));

        SpringApplicationBuilder result = application.configure(applicationBuilder);
        // 验证configure方法的逻辑
        verify(ObsSpringBootApplicationForCloud.class);
        ObsSpringBootApplicationForCloud.initBase(eq(applicationBuilder), eq("csl-svc-obs-test"));
        assert result == applicationBuilder;
    }
}

修复要点:

  • 在@PrepareForTest中加入SpringApplicationBuilder.class,支持PowerMock拦截其构造方法
  • 用whenNew拦截构造调用,返回预设的mock实例,避免创建真实对象
  • 对builder的链式调用方法做mock,返回mock实例本身支持流式调用
  • 增加调用逻辑校验,确保启动流程符合预期
启动类单元测试思路
  • 测试边界:启动类的单元测试只需要验证启动流程的调用逻辑是否符合预期,不需要真的启动Spring上下文,真实启动Spring属于集成测试的范畴,不需要用Mockito实现
  • 核心覆盖点:
    • main方法是否正确创建SpringApplicationBuilder实例
    • 自定义的初始化逻辑(如代码中的ObsSpringBootApplicationForCloud.initBase)是否被正确调用、入参是否正确
    • 是否正确指定启动类作为配置源、是否正确调用run方法传入启动参数
    • 重写的configure方法逻辑是否符合预期
  • Mock原则:
    • 仅需要拦截会触发真实Spring上下文启动的入口,不需要Mock Spring内部的所有实现类
    • 对链式调用的Builder类,Mock方法时要返回自身实例,避免空指针异常
    • 静态方法、构造方法的拦截需要配合PowerMock实现,提前在@PrepareForTest中声明对应的类

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 12:36:00