能否通过TestRule获取Spring ApplicationContext?求有效解决方法
如何通过TestRule获取Spring的ApplicationContext并拿到目标Bean
你遇到的这个问题我之前也帮不少开发者解决过——想用TestRule复用Spring测试基础设施配置,又不想靠测试类层级继承,结果想拿Environment这类Bean时一直返回null,确实挺闹心的。
先给你说清楚核心原因:你的TestRule没有和Spring测试上下文的生命周期绑定,要么是上下文还没初始化就去拿Bean,要么是压根没触发Spring上下文的加载流程。下面给你两个可行的解决方案,都是经过实践验证的。
方案一:直接用Spring官方提供的TestRule组合
Spring Test模块其实早就考虑到了这种需求,专门提供了SpringClassRule和SpringMethodRule这两个TestRule实现,完全可以替代继承AbstractJUnit4SpringContextTests的方式,还能正常用Spring的依赖注入。
代码示例
import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.rules.SpringClassRule; import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.beans.factory.annotation.Autowired; // 指定你的基础设施配置类 @ContextConfiguration(classes = YourTestConfig.class) public class SpringRuleDemoTest { // 类级别Rule,负责初始化Spring上下文 @ClassRule public static final SpringClassRule springClassRule = new SpringClassRule(); // 方法级别Rule,处理方法级的生命周期(比如注入Bean) @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); // 直接@Autowired就能拿到Bean,和继承基类的效果一样 @Autowired private Environment environment; @Test public void testEnvironmentNotNull() { assert environment != null : "Environment should not be null!"; System.out.println("当前激活的profile:" + String.join(", ", environment.getActiveProfiles())); } }
为啥这个能行?
SpringClassRule会在测试类加载时初始化Spring测试上下文,SpringMethodRule则会在每个测试方法执行前完成Bean注入、事务准备等操作,两者配合完全覆盖了Spring测试的生命周期,自然能正常拿到Environment或者其他你配置的Bean。
方案二:自定义TestRule结合TestContextManager
如果想完全自己控制TestRule的逻辑,也可以借助Spring的TestContextManager手动管理测试上下文的生命周期,主动获取ApplicationContext。
自定义Rule实现
import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.springframework.context.ApplicationContext; import org.springframework.core.env.Environment; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContextManager; public class CustomSpringContextRule implements TestRule { private final TestContextManager testContextManager; private ApplicationContext applicationContext; // 传入测试类,让TestContextManager知道要加载哪个类的Spring配置 public CustomSpringContextRule(Class<?> testClass) { this.testContextManager = new TestContextManager(testClass); } @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { // 严格遵循Spring测试上下文的生命周期顺序 testContextManager.beforeTestClass(); testContextManager.prepareTestInstance(base); // 从TestContext中获取ApplicationContext TestContext testContext = testContextManager.getTestContext(); applicationContext = testContext.getApplicationContext(); try { // 执行测试方法 base.evaluate(); } finally { // 清理上下文,避免内存泄漏 testContextManager.afterTestClass(); } } }; } // 提供获取任意Bean的方法 public <T> T getBean(Class<T> beanType) { return applicationContext.getBean(beanType); } // 快捷获取Environment的方法 public Environment getEnvironment() { return applicationContext.getEnvironment(); } }
测试类中使用自定义Rule
import org.junit.Rule; import org.junit.Test; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; @ContextConfiguration(classes = YourTestConfig.class) public class CustomRuleDemoTest { @Rule public final CustomSpringContextRule springContextRule = new CustomSpringContextRule(CustomRuleDemoTest.class); @Test public void testGetEnvironment() { Environment environment = springContextRule.getEnvironment(); assert environment != null : "Environment should not be null!"; System.out.println("配置文件中的测试属性值:" + environment.getProperty("test.app.name")); } }
注意事项
- 测试类必须加上
@ContextConfiguration指定你的Spring配置类,否则TestContextManager不知道该加载哪些Bean。 - 自定义Rule的
evaluate方法里必须严格按照beforeTestClass()->prepareTestInstance()-> 执行测试 ->afterTestClass()的顺序来,否则会出现上下文未初始化完成就拿Bean的情况,还是会返回null。
为啥你之前的实现会返回null?
大概率是这两个原因之一:
- 你的
TestRule没有触发Spring测试上下文的初始化流程,ApplicationContext还没被创建就去调用获取方法了; - 测试类没有正确配置
@ContextConfiguration,导致Spring根本没加载上下文。
内容的提问来源于stack exchange,提问作者Manuel Jordan
相关产品推荐
相关产品推荐

