【问题标题】:Reload or refresh a Spring application context inside a test method?在测试方法中重新加载或刷新 Spring 应用程序上下文?
【发布时间】:2014-09-03 09:54:51
【问题描述】:

我需要在我的测试类的单个方法中更改在我的 applicationContext 中活动的 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 对我不起作用,因为它是在类/方法执行之后而不是之前运行的,而且无论如何我都需要在运行刷新/重新加载之前执行一行代码。

有什么建议吗?我试图查看正在运行的侦听器/挂钩,但没有看到明显的位置可以插入自己以实现此行为。

【问题讨论】:

    标签: java spring testng applicationcontext spring-test


    【解决方案1】:

    根据设计,ApplicationContext 的编程刷新不受 Spring TestContext 框架明确支持。此外,测试方法并不打算刷新上下文。

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

    总之,@ActiveProfiles 支持 declarative 配置(通过 valueprofiles 属性)和 programmatic 配置(通过 resolver 属性)用于测试的活动配置文件,但仅限于测试类级别(而不是方法级别)。另一种选择是实现ApplicationContextInitializer 并通过@ContextConfiguration(initializers=...) 进行配置。

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

    最好的问候,

    Sam(Spring TestContext 框架的作者)

    【讨论】:

    • 您知道 SmartContextLoader 实现的任何示例(除了提供的类)吗?因为我试图扩展这些类,发现这真的太难了。当我需要其他 ApplicationContext 类而不是 Generic 类时,我已停止使用 @Configuration。但我也知道使用 Spring 测试工具要舒服得多……
    • 感谢您的回复山姆,很高兴有这个问题的专家。我查看了提供给AbstractGenericWebContextLoader.customizeContext 的内容,即GenericWebApplicationContextWebMergedContextConfiguration。我没有看到任何设置配置文件的方法,你能给我指点吗?
    • GenericWebApplicationContext 上,您可以调用context.getEnvironment().setActiveProfiles(...) 等。
    • 关于 SmartContextLoader 实现的示例,您可以在 Google 上搜索“实现 SmartContextLoader”(包括引号),但这不会返回很多结果,因为大多数人要么扩展 AbstractContextLoaderAbstractGenericContextLoader .例如,Spring Boot 引入了自己的SpringApplicationContextLoader,它直接扩展了AbstractContextLoader
    • 感谢 Sam,我很欣赏出色的答案和出色的框架。我们今天只是在内部聊天,谈论在此之前进行测试会有多痛苦。感谢您的辛勤工作!
    【解决方案2】:

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

    我不能 100% 确定此方法是否适合您:它需要 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
        }
    
    }
    

    【讨论】:

      【解决方案3】:

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

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

      编辑:

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

      我知道这并不能真正回答您的问题,但您可以将其视为一种解决方法。

      【讨论】:

      • 同意,虽然我不知道如何影响创建的应用程序上下文类的选择。默认情况下,Spring 的测试基础设施正在创建一个 GenericWebApplicationContext(GenericApplicationContext 的子类在上面的异常中可见)。
      • 哎呀,没有注意到你使用的是@ContextConfiguration ...我刚刚更新了我的帖子。
      猜你喜欢
      • 2012-10-28
      • 1970-01-01
      • 2017-01-16
      • 2011-09-17
      • 1970-01-01
      • 1970-01-01
      • 2016-10-31
      • 1970-01-01
      相关资源
      最近更新 更多