带有配置文件的 Spring 集成测试

Posted

技术标签:

【中文标题】带有配置文件的 Spring 集成测试【英文标题】:Spring integration tests with profile 【发布时间】:2013-12-31 08:19:41 【问题描述】:

在我们的 Spring Web 应用程序中,我们使用 Spring bean 配置文件来区分三个场景:开发、集成和生产。我们使用它们连接到不同的数据库或设置其他常量。

使用 Spring bean 配置文件非常适合更改 Web 应用程序环境。

我们遇到的问题是当我们的集成测试代码需要针对环境进行更改时。在这些情况下,集成测试会加载 Web 应用程序的应用程序上下文。这样我们就不必重新定义数据库连接、常量等(应用 DRY 原则)。

我们如下设置集成测试。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ["classpath:applicationContext.xml"])
public class MyTestIT

   @Autowired
   @Qualifier("myRemoteURL")  // a value from the web-app's applicationContext.xml
   private String remoteURL;
   ...
 

我可以使用@ActiveProfiles 让它在本地运行,但这是硬编码的,会导致我们的测试在构建服务器上失败。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ["classpath:applicationContext.xml"])
@ActiveProfiles("development")
public class MyTestIT
 ... 

我也尝试过使用@WebAppConfiguration,希望它能以某种方式从 Maven 导入spring.profiles.active 属性,但这不起作用。

另外需要注意的是,我们还需要配置我们的代码,以便开发人员可以运行 Web 应用程序,然后使用 IntelliJ 的测试运行程序(或其他 IDE)运行测试。这对于调试集成测试要容易得多。

【问题讨论】:

【参考方案1】:

如果您想避免对配置文件进行硬编码,您可能需要使用 system property spring.profiles.active 并将其设置为您在该特定环境中需要的任何内容,例如我们有针对不同环境的“dev”、“stage”和“prod”配置文件;我们还有一个“test”、“test-local”和“test-server”配置文件用于我们的测试。

请记住,通过使用逗号分隔值列表,您可以在该系统属性中拥有多个配置文件,例如“测试,测试-qa”。

您可以在 maven surefire plugin 的 maven 项目中指定系统属性,或者像这样传递它们:

mvn -DargLine="-DpropertyName=propertyValue"

【讨论】:

【参考方案2】:

正如其他人已经指出的那样,您可以选择使用 Maven 设置spring.profiles.active 系统属性,确保使用@ActiveProfiles,但这对于在其中运行的测试不方便IDE。

对于设置活动配置文件的编程方式,您有几个选项。

    Spring 3.1:编写自定义 ContextLoader,通过在上下文的 Environment 中设置活动配置文件来准备上下文。 Spring 3.2:自定义ContextLoader 仍然是一个选项,但更好的选择是实现ApplicationContextInitializer 并通过@ContextConfigurationinitializers 属性对其进行配置。您的自定义初始化程序可以通过以编程方式设置活动配置文件来配置 Environment。 Spring 4.0:上述选项仍然存在;但是,从 Spring Framework 4.0 开始,有一个新的专用 ActiveProfilesResolver API 正是为此目的:以编程方式确定要在测试中使用的活动配置文件集。可以通过@ActiveProfilesresolver 属性注册ActiveProfilesResolver

问候,

Sam(Spring TestContext 框架的作者)

【讨论】:

我正在使用 Spring 3.2,第 2 项非常适合它。我将ApplicationContextInitializer 设置为调用configurableApplicationContext.getEnvironment().setDefaultProfiles("development");,当我通过IntelliJ 运行测试时,它会运行开发配置文件。 有没有使用ActiveProfilesResolver的例子?我真的不知道如何使用它 是的,在Spring框架参考手册的Testing chapter中有一个如何使用自定义ActiveProfilesResolver的例子。 还有在测试类中使用系统属性覆盖@ActiveProfiles 的错误:jira.spring.io/browse/SPR-8982 @SamBrannen 还有第四个选项。编写您自己的自定义SpringJUnit4ClassRunner。我必须这样做,因为 Spring 会非常急切地加载公共日志记录,如果您想以编程方式配置日志记录,这将非常烦人。当然,您必须自己维护 ClassRunner,但至少您可以控制初始化和缓存。【参考方案3】:

正如@ElderMael 提到的,您可以使用 maven surefire 插件的 argLine 属性。通常,当我需要使用不同的特定 Spring 配置文件运行所有测试时,我会定义额外的 maven 配置文件。示例:

<profiles>
    <profile>
        <id>foo</id>
        <dependencies>
            <!-- additional dependencies if needed, i.e. database drivers ->
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <argLine>-Dspring.profiles.active=foo</argLine>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

通过这种方法,您可以通过 maven 命令轻松运行所有已激活配置文件的测试:

mvn clean test -Pfoo

@ActiveProfile 注释很好,但有时我们需要使用激活的特定配置文件和硬编码的@ActiveProfile 参数运行所有测试,这是一个问题。

例如:默认情况下使用 H2 内存数据库进行集成测试,但有时您希望在“真实”数据库上运行测试。您可以定义该额外的 Maven 配置文件并定义 Jenkins 作业。使用 SpringBoot,您还可以将其他属性添加到名为 application-foo.yml(或属性)的测试/资源中,这些属性将被考虑在内。

【讨论】:

【参考方案4】:

我遇到了类似的问题:我想使用默认配置文件运行我的所有集成测试,但允许用户使用代表不同环境甚至数据库风格的配置文件进行覆盖,而无需更改 @ActiveProfiles 值。如果您使用带有自定义 ActiveProfilesResolver 的 Spring 4.1+,这是可行的。

此示例解析器查找系统属性 spring.profiles.active,如果不存在,它将委托给默认解析器,该解析器仅使用 @ActiveProfiles 注释。

public class SystemPropertyActiveProfileResolver implements ActiveProfilesResolver 

private final DefaultActiveProfilesResolver defaultActiveProfilesResolver = new DefaultActiveProfilesResolver();

@Override
public String[] resolve(Class<?> testClass) 

    if(System.getProperties().containsKey("spring.profiles.active")) 

        final String profiles = System.getProperty("spring.profiles.active");
        return profiles.split("\\s*,\\s*");

     else 

        return defaultActiveProfilesResolver.resolve(testClass);
    

在你的测试类中,你会这样使用它:

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles( profiles="h2","xyz",
resolver=SystemPropertyActiveProfileResolver.class)
public class MyTest  

除了检查系统属性是否存在之外,您当然可以使用其他方法来设置活动配置文件。希望这对某人有所帮助。

【讨论】:

【参考方案5】:

这个问题有很多方面。 就我而言,对 build.gradle 的一个简单补充已经有所帮助:

test  systemProperties = System.properties 

【讨论】:

以上是关于带有配置文件的 Spring 集成测试的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Boot 在单元测试中配置集成测试或使用 Spring JDBC?

当在命令行中未指定配置文件时,Spring Boot 集成测试无法获取默认配置文件并引发错误

使用 Spring Boot 进行 Kotlin 集成测试

Spring集成测试

Spring Boot集成测试无法在命令行未指定配置文件时获取默认配置文件并抛出错误

如何使用带有 gradle 的多个嵌入式服务器运行 spring-boot 集成测试