弹簧配置文件和测试

Posted

技术标签:

【中文标题】弹簧配置文件和测试【英文标题】:Spring profiles and testing 【发布时间】:2012-11-02 01:56:19 【问题描述】:

我有一个 Web 应用程序,其中存在一个典型问题,即它需要针对不同环境使用不同的配置文件。一些配置作为 JNDI 数据源放置在应用服务器中,但是一些配置保留在属性文件中。

因此我想使用 Spring 配置文件功能。

我的问题是我没有让测试用例运行。

context.xml:

<context:property-placeholder 
  location="classpath:META-INF/spring/config_$spring.profiles.active.properties"/>

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(
    TestPreperationExecutionListener.class
    )
@Transactional
@ActiveProfiles(profiles = "localtest")
@ContextConfiguration(locations = 
    "classpath:context.xml" )
public class TestContext 

  @Test
  public void testContext()

  

问题似乎是加载配置文件的变量没有解决:

Caused by: java.io.FileNotFoundException: class path resource [META-INF/spring/config_$spring.profiles.active.properties] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:157)
at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:181)
at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:161)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.postProcessBeanFactory(PropertySourcesPlaceholderConfigurer.java:138)
... 31 more

应使用@ActiveProfile 注释设置当前配置文件。由于这是一个测试用例,我将无法使用web.xml。如果可能的话,我也想避免运行时选项。测试应按原样运行(如果可能)。

如何正确激活配置文件?是否可以使用 context.xml 设置配置文件?我可以在实际调用正常上下文的 test-context.xml 中声明变量吗?

【问题讨论】:

【参考方案1】:

我可以推荐这样做吗,像这样定义你的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(
    TestPreperationExecutionListener.class
    )
@Transactional
@ActiveProfiles(profiles = "localtest")
@ContextConfiguration
public class TestContext 

  @Test
  public void testContext()

  

  @Configuration
  @PropertySource("classpath:/myprops.properties")
  @ImportResource("classpath:context.xml" )
  public static class MyContextConfiguration

  

在 myprops.properties 文件中包含以下内容:

spring.profiles.active=localtest

有了这个你的第二个属性文件应该得到解决:

META-INF/spring/config_$spring.profiles.active.properties

【讨论】:

这种方法不干净,您必须转到应该更改活动配置文件值的属性文件。【参考方案2】:

看着 Biju 的回答,我找到了一个可行的解决方案。

我创建了一个额外的上下文文件test-context.xml

<context:property-placeholder location="classpath:config/spring-test.properties"/>

包含简介:

spring.profiles.active=localtest

并加载测试:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(
    TestPreperationExecutionListener.class
    )
@Transactional
@ActiveProfiles(profiles = "localtest")
@ContextConfiguration(locations = 
    "classpath:config/test-context.xml" )
public class TestContext 

  @Test
  public void testContext()

  

这在创建多个测试用例时节省了一些工作。

【讨论】:

所以每次你想运行一个测试你必须去 test-context.xml 并更改属性文件路径,如果想加载另一个属性?这与 Spring Profiles 的想法相矛盾。看看我在这种情况下的解决方案。 @CodeBusta 不,它是一个 test-context.xml,仅用于加载常规上下文的测试。【参考方案3】:
public class LoginTest extends BaseTest 
    @Test
    public void exampleTest( ) 
        // Test
    

从基础测试类继承(这个例子是testng而不是jUnit,但ActiveProfiles是一样的):

@ContextConfiguration(locations =  "classpath:spring-test-config.xml" )
@ActiveProfiles(resolver = MyActiveProfileResolver.class)
public class BaseTest extends AbstractTestNGSpringContextTests  

MyActiveProfileResolver 可以包含确定使用哪个配置文件所需的任何逻辑:

public class MyActiveProfileResolver implements ActiveProfilesResolver 
    @Override
    public String[] resolve(Class<?> aClass) 
        // This can contain any custom logic to determine which profiles to use
        return new String[]  "exampleProfile" ;
    

这会设置配置文件,然后用于解决测试所需的依赖关系。

【讨论】:

【参考方案4】:

这里最好的方法是删除@ActiveProfiles 注释并执行以下操作:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(
    TestPreperationExecutionListener.class
    )
@Transactional
@ContextConfiguration(locations = 
    "classpath:config/test-context.xml" )
public class TestContext 

  @BeforeClass
  public static void setSystemProperty() 
        Properties properties = System.getProperties();
        properties.setProperty("spring.profiles.active", "localtest");
  

  @AfterClass
  public static void unsetSystemProperty() 
        System.clearProperty("spring.profiles.active");
  

  @Test
  public void testContext()

  

你的 test-context.xml 应该有以下内容:

<context:property-placeholder 
  location="classpath:META-INF/spring/config_$spring.profiles.active.properties"/>

【讨论】:

在@AfterClass 中使用静态属性而不清理它们会导致严重的、难以分析的问题。 好吧,正如我所说:至少在 AfterClass 方法中通过 System.clearProperty() 清理属性。或者使用一些规则,如stefanbirkner.github.io/system-rules 我明白了,但是在这个特定示例中清理的原因是什么?每个测试都使用自己的上下文运行,因此您不必清理系统属性。如果我错了纠正我。我刚刚添加了请求的清理部分,但我不明白这里为什么需要它。 不要这样做。我会告诉你这是一种极其不可靠的方法。问题在于没有使用 Spring 的特殊 @ActiveProfiles 它将缓存应用程序上下文。缓存键基于@ContextConfiguration(locations = )。至于更改系统属性,如果您想同时运行测试,那么您真的是 *#!$#@ 自己,这是完全错误的:“每个测试都使用自己的上下文运行,因此您不必清理系统属性" 。只有当你有 Surefire/JUnit 分叉时才有效。 只是不要使用这种方法。【参考方案5】:

@EnableConfigurationProperties 需要在那里(你也可以注释你的测试类),来自 test/resources 的 application-localtest.yml 将被加载。 使用 jUnit5 的示例

@ExtendWith(SpringExtension.class)
@EnableConfigurationProperties
@ContextConfiguration(classes = YourClasses, initializers = ConfigFileApplicationContextInitializer.class)
@ActiveProfiles(profiles = "localtest")
class TestActiveProfile 

    @Test
    void testActiveProfile()

    

【讨论】:

你能使用-Dspring.profiles.active=name从命令行加载配置吗?似乎上述建议将您修复到特定配置中。

以上是关于弹簧配置文件和测试的主要内容,如果未能解决你的问题,请参考以下文章

仅在设置了特定的弹簧配置文件时才运行集成测试

是否可以在 WebMvcTest 中激活弹簧配置文件

如何在某个弹簧配置文件中禁用飞路?

弹簧负载产品配置文件

Maven 配置文件属性不会覆盖弹簧配置中的值

如何使用 Maven 配置文件设置弹簧活动配置文件