在测试方法中重新加载或刷新Spring应用程序上下文?

17

我需要在测试类的一个方法中更改应用程序上下文中激活的Spring配置文件,并且为此需要在刷新上下文之前运行一行代码,因为我正在使用ProfileResolver。我尝试了以下内容:

@WebAppConfiguration
@ContextConfiguration(locations = {"/web/WEB-INF/spring.xml"})
@ActiveProfiles(resolver = BaseActiveProfilesResolverTest.class)
public class ControllerTest extends AbstractTestNGSpringContextTests {
    @Test
    public void test() throws Exception {
        codeToSetActiveProfiles(...);
        ((ConfigurableApplicationContext)this.applicationContext).refresh();
        ... tests here ...
        codeToSetActiveProfiles(... back to prior profiles ...);
        ... ideally refresh/reload the context for future tests
    }
}

但是我得到了:

java.lang.IllegalStateException: GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once

DirtiesContext对我来说不起作用,因为它是在类/方法执行之后运行的,而不是之前,而且我需要在刷新/重新加载之前执行一行代码。

有什么建议吗?我尝试浏览正在运行的监听器/钩子,但是我没有看到一个明显的位置可以插入自己来实现这种行为。

3个回答

19

按照设计,Spring TestContext框架不明确支持ApplicationContext的程序化刷新。此外,测试方法也不应该刷新上下文。

因此,我建议您重新评估您是否需要进行刷新,并考虑替代方案,例如将需要不同活动配置的测试方法放置在专用测试类中。

总之,@ActiveProfiles支持测试活动配置的声明式配置(通过value和profiles属性)以及程序化配置(通过resolver属性),但仅在测试类级别而非测试方法级别。另一种选择是实现一个ApplicationContextInitializer,并通过@ContextConfiguration(initializers=...)进行配置。

在刷新ApplicationContext之前,唯一的另一种影响方式是实现一个SmartContextLoader或扩展其中一个提供的类,并通过@ContextConfiguration(loader=...)进行配置。例如,AbstractGenericContextLoader.customizeContext()允许在bean定义加载到上下文中但上下文刷新之前“自定义由加载器创建的GenericApplicationContext”。

敬祝

Sam(Spring TestContext框架作者)


你知道任何SmartContextLoader实现的例子吗(除了提供的类)?因为我尝试扩展这些类,发现真的太难了。当我需要除了通用类之外的其他ApplicationContext类时,我已经停止使用@Configuration。但我也知道使用Spring测试工具会更加舒适... - Serge Ballesta
感谢您的回复,Sam。很高兴在这个问题上有一个专家。我查看了AbstractGenericWebContextLoader.customizeContext提供的内容,其中包括GenericWebApplicationContextWebMergedContextConfiguration。我没有看到任何设置配置文件的方法,您能给我指点一下吗? - David E
GenericWebApplicationContext上,您可以调用context.getEnvironment().setActiveProfiles(...)等方法。 - Sam Brannen
关于SmartContextLoader实现的示例,您可以在Google上搜索“implements SmartContextLoader”(包括引号),但这不会返回太多结果,因为大多数人要么扩展AbstractContextLoaderAbstractGenericContextLoader。例如,Spring Boot引入了自己的SpringApplicationContextLoader,它直接扩展了AbstractContextLoader - Sam Brannen
谢谢Sam,我很感激你提供的出色答案和优秀框架。我们今天内部聊天时刚刚谈到在使用它之前进行测试会有多么痛苦。感谢你的辛勤工作! - David E
非常感谢,David。还有谢谢你的赞美!我真的很感激。愉快编码 :) - Sam Brannen

5

有一个简单的技巧可以触发上下文刷新 - 使用org.springframework.cloud.context.refresh.ContextRefresher

我不确定这种方法是否适合您:它需要spring-cloud-context依赖项。但是,这可能只是作为test依赖项添加,而不会泄漏到生产类路径中。

要使用此刷新程序,还需要导入org.springframework.cloud.autoconfigure.RefreshAutoConfiguration配置,该配置将RefreshScope作用域添加到applicationContext中,在幕后实际完成工作。

因此,请按以下方式修改测试:

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
import org.springframework.cloud.context.refresh.ContextRefresher;    
// your other imports


@WebAppConfiguration
@ContextConfiguration(locations = {"/web/WEB-INF/spring.xml"}, classes = RefreshAutoConfiguration.class)
@ActiveProfiles(resolver = BaseActiveProfilesResolverTest.class)
public class ControllerTest extends AbstractTestNGSpringContextTests {

    @Autowired
    private ContextRefresher contextRefresher;

    @Test
    public void test() throws Exception {
        // doSmth before
        contextRefresher.refresh();
        // context is refreshed - continue testing
    }

}

1

并非所有的应用程序上下文都支持多个refresh。根据AbstractRefreshableApplicationContext的javadoc,只有它的子类或AbstractRefreshableWebApplicationContext的子类接受多次refresh...而GenericApplicationContext不是其中之一。

您应该使用另一个类作为您的ApplicationContext来支持热刷新。

编辑:

由于您正在使用@ContextConfiguration注释,因此您应该使用自定义的ContextLoaderSmartContextLoader实现来强制Spring使用一个不那么愚蠢的ApplicationContext。但我从未找到过一种干净整洁的方法来做到这一点。所以当我在我的测试类中需要一个XmlWebApplicationContext时,我不使用@ContextConfiguration,而是在@Before方法或测试开始时手动创建和刷新我的上下文。

我承认这并没有真正回答您的问题,但您可以将其视为一种解决方法。


同意,尽管我不知道如何影响所创建的应用程序上下文类的选择。默认情况下,Spring的测试基础设施会创建一个GenericWebApplicationContext(在上面的异常中看到的GenericApplicationContext的子类)。 - David E
抱歉,没注意到你正在使用@ContextConfiguration...我刚刚更新了我的帖子。 - Serge Ballesta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接