【问题标题】:在 Junit Test 中覆盖默认 Spring-Boot application.properties 设置
【发布时间】:2015-06-22 13:05:12
【问题描述】:

我有一个 Spring-Boot 应用程序,其中默认属性设置在类路径 (src/main/resources/application.properties) 中的 application.properties 文件中。

我想用 test.properties 文件 (src/test/resources/test.properties) 中声明的属性覆盖我的 JUnit 测试中的一些默认设置

我通常有一个专门的配置类用于我的 Junit 测试,例如

package foo.bar.test;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(CoreConfig.class)
@EnableAutoConfiguration
public class TestConfig {

}

我最初认为在 TestConfig 类中使用 @PropertySource("classpath:test.properties") 可以解决问题,但这些属性不会覆盖 application.properties 设置(请参阅 Spring-Boot 参考文档 - 23. Externalized Configuration)。

然后我在调用测试时尝试使用-Dspring.config.location=classpath:test.properties。那是成功的——但我不想为每个测试执行设置这个系统属性。因此我把它放在代码中

@Configuration
@Import(CoreConfig.class)
@EnableAutoConfiguration
public class TestConfig {

  static {
    System.setProperty("spring.config.location", "classpath:test.properties");
  }

}

不幸的是,这又没有成功。

关于如何在 JUnit 测试中用 test.properties 覆盖 application.properties 设置必须有一个简单的解决方案,我一定忽略了。

【问题讨论】:

标签: java unit-testing spring-boot


【解决方案1】:

你可以在src/test/resources/META-INF中创建一个spring.factories文件,在src/test/java中创建一个EnvironmentPostProcessor实现类。
spring.factorieslike

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.test.YourTestPropertiesConfig

YourTestPropertiesConfig.java点赞

package com.example.test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;

import java.util.HashMap;
import java.util.Map;

public class YourTestPropertiesConfig implements EnvironmentPostProcessor {
    private static final Map<String, Object> testProperties = new HashMap<>();
    private static final Set<String> testPropertiesFile = new HashSet<>();

    static {
    //Add the properties you need to take effect globally in the test directly here.
        testProperties.put("spring.jackson.time-zone", "GMT");
        testPropertiesFile.add("classpath:test.properties");
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        environment.getPropertySources().addFirst(new MapPropertySource("TestProperties", testProperties));
        for (String location : testPropertiesFile) {
            try {
                environment.getPropertySources().addFirst(new ResourcePropertySource(location));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void addProperty(String key, Object value) {
        testProperties.put(key, value);
    }

    public static void addProperty(String location) {
        testPropertiesFile.add(location);
    }
}

【讨论】:

    【解决方案2】:

    我觉得你也可以用这个:

    @TestPropertySource(properties = "spring.config.additional-location=classpath:application-test.yml")
    

    当使用 spring.config.additional-location 配置自定义配置位置时,除了默认位置之外,还会使用它们。

    文件优先

    详情请咨询here

    【讨论】:

      【解决方案3】:

      TLDR:

      所以我所做的是拥有标准的src/main/resources/application.propertiessrc/test/resources/application-default.properties,我在其中覆盖了我所有测试的一些设置。

      对于电力开发者:

      为了更轻松地更改/使用不同的弹簧配置文件,我现在有一个application-default.yaml,它声明了我要使用的配置文件。 此文件未提交,因此每个开发人员都可以选择激活他/她正在开发的配置文件和需求(例如功能)的方式。

      spring:
        profiles:
          include:
            - local
            - devlocal
            - wip
      #      - kafka@docker
      
      ---
      spring.profiles: wip
      # ... overriding properties 
      
      

      整个故事

      我遇到了同样的问题,到目前为止也没有使用配置文件。现在必须这样做并记住声明配置文件似乎很麻烦 - 这很容易忘记。

      诀窍在于,利用特定配置文件 application-&lt;profile&gt;.properties 覆盖通用配置文件中的设置。见https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties

      【讨论】:

      【解决方案4】:

      如果你和我一样,在 src/main/resourcessrc/test/resources 中拥有相同的 application.properties,并且你想知道为什么你的测试文件夹中的 application.properties不是覆盖您的主要资源中的application.properties,继续阅读...

      简单解释:

      如果您在src/main/resources 下有application.properties,在src/test/resources 下有相同的application.propertiesapplication.properties 被选中,取决于您运行测试的方式。文件夹 structure src/main/resourcessrc/test/resources 是一个 Maven 架构约定,所以如果你像 mvnw test 甚至 gradlew test 那样运行测试,src/test/resources 中的 application.properties 将得到拾取,因为 test 类路径将在 main 类路径之前。但是,如果你在 Eclipse/STS 中像 Run as JUnit Test 这样运行测试,src/main/resources 中的 application.properties 将被选中,因为 main 类路径在 test 类路径之前。

      您可以打开菜单栏Run &gt; Run Configurations &gt; JUnit &gt; *your_run_configuration* &gt; Click on "Show Command Line"查看。

      您会看到如下内容:

      XXXbin\javaw.exe -ea -Dfile.encoding=UTF-8 -classpath
      XXX\workspace-spring-tool-suite-4-4.5.1.RELEASE\project_name\bin\main;
      XXX\workspace-spring-tool-suite-4-4.5.1.RELEASE\project_name\bin\test;

      您是否看到 classpath xxx\main 先出现,然后是 xxx\test?对,都是关于类路径的 :-)

      旁注: 请注意,在启动配置中覆盖的属性(例如在 Spring Tool Suite IDE 中)优先于 application.properties。

      更改顺序:

      现在,一切都可以在 Spring 中进行配置。您可以更改构建类路径,使 xxx\test 先出现,然后是 xxx\main

      只需转到Project &gt; Properties &gt; Java Build Path &gt; Order and Export,通过将任何 test 文件夹放在首位来更改构建类路径顺序,例如:

      就是这样!

      更好的解决方案

      不过,更好的解决方案是在测试时激活src/test/resources/application-{profile}.properties(可以测试profile),例如src/main/resources/application.properties中的以下内容:

      spring.profiles.active=test

      这更简洁,让您可以完全控制在做什么时激活什么配置文件。

      【讨论】:

      • 我发现这种方法缺乏,因为在您的测试中定义配置文件将删除通过 -Dspring.profiles.active=... 在 CI 和其他可能需要您可能不想要的其他配置的环境中即时调整它的选项通过-D 覆盖,因为这变得非常乏味。毕竟,只有 集成 测试才需要启动 spring 上下文,并且这些测试可能依赖于外部资源,这些资源与它们运行的​​环境不同。
      • 嘿@elonderin,感谢您的评论。如果有大家通用的配置,我将它们插入application.properties。此后,如果我在local 上开发并做testspring.profiles.active=local, test。如果我在 pre-prod,它变成spring.profiles.active=pre-prod, test。这是你想要的吗?
      【解决方案5】:

      Spring Boot 会自动加载src/test/resources/application.properties,如果使用了以下注解

      @RunWith(SpringRunner.class)
      @SpringBootTest
      

      因此,将 test.properties 重命名为 application.properties 以使用自动配置。

      如果您需要加载属性文件(到环境中),您也可以使用以下内容,如here

      所述
      @RunWith(SpringRunner.class)
      @ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class) 
      

      [更新:覆盖某些属性以进行测试]

      1. 添加src/main/resources/application-test.properties
      2. @ActiveProfiles("test")注释测试类。

      这会将application.properties然后 application-test.properties 属性加载到测试用例的应用程序上下文中,根据here 定义的规则。

      演示 - https://github.com/mohnish82/so-spring-boot-testprops

      【讨论】:

      • 不确定在类路径中有两个application.properties 文件是否是个好主意(一个在src/main/resources 中,一个在src/test/resources 中)。谁保证两者都会被录取,哪个会先被录取?
      • @FrVaBe Spring 将保证!始终加载主配置文件属性。然后在测试阶段,加载测试属性,添加/覆盖新的/现有的属性。如果您不喜欢保留两个同名文件,那么您可以在src/main/resources 中添加application-test.properties,并在测试用例中指定test 作为活动配置文件。
      • Spring 不做任何保证。构建工具将在测试期间使用测试资源来支持主要资源。但在测试 application.properties 的情况下,主 application.properties 将被忽略。这不是我想要的,因为主要的包含几个有用的默认值,我只需要在测试期间覆盖其中的一些(我不想在测试部分复制整个文件)。见here
      • 你是对的,在测试阶段只加载src/test/resources/application.properties中定义的属性,src/main/resources/application.properties被忽略。
      • 如果您到目前为止不使用配置文件,则不需要专用的“测试”配置文件。只需将您的测试属性命名为application-default.properties,它们就会被考虑,因为您正在自动运行“默认”配置文件(如果未声明任何其他配置文件)。
      【解决方案6】:

      如果您使用的是 Spring 5.2.5 和 Spring Boot 2.2.6,并且只想覆盖几个属性而不是整个文件。您可以使用新注解:@DynamicPropertySource

      @SpringBootTest
      @Testcontainers
      class ExampleIntegrationTests {
      
          @Container
          static Neo4jContainer<?> neo4j = new Neo4jContainer<>();
      
          @DynamicPropertySource
          static void neo4jProperties(DynamicPropertyRegistry registry) {
              registry.add("spring.data.neo4j.uri", neo4j::getBoltUrl);
          }
      }
      

      【讨论】:

        【解决方案7】:
        I just configured min as the following :
        
        spring.h2.console.enabled=true
        spring.h2.console.path=/h2-console
        
        
        # changing the name of my data base for testing
        spring.datasource.url= jdbc:h2:mem:mockedDB
        spring.datasource.username=sa
        spring.datasource.password=sa
        
        
        
        # in testing i don`t need to know the port
        
        #Feature that determines what happens when no accessors are found for a type
        #(and there are no annotations to indicate it is meant to be serialized).
        spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false`enter code here`
        

        【讨论】:

          【解决方案8】:

          如果您使用@SpringBootTest 注释,另一种适合覆盖测试中的一些属性的方法:

          @SpringBootTest(properties = {"propA=valueA", "propB=valueB"})
          

          【讨论】:

          • SpringBootTest 是否加载 application.properties 文件?
          • @TuGordoBello 是的
          【解决方案9】:

          您还可以在编写 JUnit 的 src/test/resources 中创建 application.properties 文件。

          【讨论】:

          • 这有什么帮助? ^^
          • 这是他不想要的……
          【解决方案10】:

          您还可以使用meta-annotations 将配置外部化。例如:

          @RunWith(SpringJUnit4ClassRunner.class)
          @DefaultTestAnnotations
          public class ExampleApplicationTests { 
             ...
          }
          
          @Retention(RetentionPolicy.RUNTIME)
          @Target(ElementType.TYPE)
          @SpringApplicationConfiguration(classes = ExampleApplication.class)
          @TestPropertySource(locations="classpath:test.properties")
          public @interface DefaultTestAnnotations { }
          

          【讨论】:

            【解决方案11】:

            否则我们可能会更改默认属性配置器名称,设置属性spring.config.name=test,然后拥有类路径资源 src/test/test.properties 我们的本机实例 org.springframework.boot.SpringApplication 将从这个单独的 test.properties 自动配置,忽略应用程序属性;

            好处:测试的自动配置;

            缺点:在 C.I. 暴露“spring.config.name”属性。层

            参考:http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

            spring.config.name=application #配置文件名

            【讨论】:

            • 忽略 application.properties 对我来说不是一个选项,因为我只想覆盖测试中的 一些 原始配置值。
            • 我一直在寻找一种方法来进行不加载 src/main/resources/application.properties 的单个测试,就是这样。创建一个文件: src/test/resources/empty.properties 并将注释添加到应该忽略主要属性的测试中。 @TestPropertySource(properties="spring.config.name=empty")
            • 如何为每个junit测试方法设置具体的属性值?
            【解决方案12】:

            您可以使用@TestPropertySource 覆盖application.properties 中的值。从它的javadoc:

            测试属性源可用于选择性地覆盖系统和应用程序属性源中定义的属性

            例如:

            @RunWith(SpringJUnit4ClassRunner.class)
            @SpringApplicationConfiguration(classes = ExampleApplication.class)
            @TestPropertySource(locations="classpath:test.properties")
            public class ExampleApplicationTests {
            
            }
            

            【讨论】:

            • 就是这样。谢谢。不幸的是,在 ExampleApplication.class 上使用时它不起作用,所以我必须在每个测试类上设置它。对吗?
            • 还要注意@TestPropertySource 可以接受properties 参数来覆盖某些内联属性,例如@TestPropertySource(properties = "myConf.myProp=valueInTest"),如果您不想要一个全新的属性文件,这很有用。
            • 您可以在一个数组中指定多个文件,也可以指定文件系统上的文件(但请记住它们可能不适用于 CI 服务器):@TestPropertySource(locations={"file:C:/dev/...","classpath:test.properties"})
            • 请注意,@SpringApplicationConfiguration 已被弃用,您应该使用@SpringBootTest
            • @Stefan 如果您激活了test 配置文件,则可以使用它。
            猜你喜欢
            • 2015-09-12
            • 1970-01-01
            • 2020-05-03
            • 2017-01-29
            • 2016-06-06
            • 2019-05-15
            • 2016-02-12
            • 2018-06-13
            • 2016-08-20
            相关资源
            最近更新 更多