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

非Spring Boot的Spring应用如何Mock静态final ApplicationContext做单元测试

解决方案

方案1:重构代码(优先推荐)

你当前的写法本身违反了Spring依赖注入的设计原则:类内部硬编码初始化ApplicationContext,主动拉取依赖,而非通过外部注入依赖,天生对单元测试不友好,同时还会存在上下文重复初始化、环境切换不灵活等问题。重构后可以从根本上解决测试问题:

  1. 不需要持有ApplicationContext的场景:直接注入依赖的Bean,完全解耦Spring上下文
public class MyClass {
    private final DependencyBean dependencyBean;

    // 构造器直接注入依赖,不需要感知ApplicationContext存在
    public MyClass(DependencyBean dependencyBean) {
        this.dependencyBean = dependencyBean;
    }
    
    // 类的其他方法
}

单元测试时直接将Mock好的依赖传入构造器即可,不需要任何Spring相关的初始化逻辑。
2. 确实需要使用ApplicationContext的场景:通过构造器注入上下文,而非内部初始化

public class MyClass {
    private final ApplicationContext context;

    // 由外部传入ApplicationContext实例
    public MyClass(ApplicationContext context) {
        this.context = context;
        // 从context中获取依赖的Bean
    }
    
    // 类的其他方法
}

单元测试时直接传入Mock好的ApplicationContext实例即可。

方案2:不修改业务代码,使用Mock工具修改静态final字段(临时适配方案)

如果因为历史遗留问题无法修改业务代码,可以利用Mock框架的能力修改静态final字段的值,绕过真实ApplicationContext的初始化逻辑,Mockito 3.4+ 和 PowerMock都支持该能力:

Mockito 3.4+ 实现示例

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.mockito.Mockito.*;

public class MyClassTest {
    @Test
    void testMyClassMethod() {
        // 初始化Mock的上下文和依赖Bean
        ApplicationContext mockContext = mock(ApplicationContext.class);
        DependencyBean mockBean = mock(DependencyBean.class);
        when(mockContext.getBean(DependencyBean.class)).thenReturn(mockBean);

        // 替换AnnotationConfigApplicationContext的构造逻辑,返回Mock的上下文
        try (MockedStatic<AnnotationConfigApplicationContext> mockedAppContext = mockStatic(AnnotationConfigApplicationContext.class)) {
            mockedAppContext.when(() -> new AnnotationConfigApplicationContext(AppContext.class))
                    .thenReturn(mockContext);
            // 此时初始化MyClass,内部static字段会被赋值为Mock的context
            MyClass myClass = new MyClass();
            // 执行你的测试逻辑
        }
    }
}

PowerMock 实现示例

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static org.mockito.Mockito.*;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
    @Test
    public void testMyClassMethod() throws Exception {
        // 初始化Mock的上下文和依赖Bean
        ApplicationContext mockContext = mock(ApplicationContext.class);
        DependencyBean mockBean = mock(DependencyBean.class);
        when(mockContext.getBean(DependencyBean.class)).thenReturn(mockBean);

        // 反射修改static final字段的值
        Field contextField = MyClass.class.getDeclaredField("context");
        contextField.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(contextField, contextField.getModifiers() & ~Modifier.FINAL);
        contextField.set(null, mockContext);

        // 初始化MyClass执行测试
        MyClass myClass = new MyClass();
        // 执行你的测试逻辑
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 20:45:04