在 Junit 测试中覆盖默认 Spring-Boot application.properties 设置

Override default Spring-Boot application.properties settings in Junit Test

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

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

我通常有一个专门的配置 Class 用于我的 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 class 中使用 @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");
  }

}

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

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

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

test property sources can be used to selectively override properties defined in system and application property sources

例如:

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

}

您还可以使用 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 { }

否则我们可能会更改默认的 属性 配置器名称,设置 属性 spring.config.name=test 然后拥有 class-path 资源 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 # Config file name

Spring 引导自动加载 src/test/resources/application.properties,如果使用以下注释

@RunWith(SpringRunner.class)
@SpringBootTest

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

If you only need to load the properties file (into the Environment) you can also use the following, as explained here

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

[更新:重写某些属性以进行测试]

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

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

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

TLDR:

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

对于电源开发人员:

为了 change/use 更容易地改变 spring 配置文件,我现在有一个 application-default.yaml 来声明我想使用的配置文件。 此文件未提交,因此每个开发人员都可以选择激活配置文件和需求(例如功能)的方式 he/she 正在处理。

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

---
spring.profiles: wip
# ... overriding properties 

整个故事

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

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

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

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

@SpringBootTest(properties = {"propA=valueA", "propB=valueB"})
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`

如果您像我一样,在 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 testsrc/test/resources 中的 application.properties 将被拾取,因为 test classpath 将在 main[= 之前90=] class路径。但是,如果你 运行 你的测试像 Eclipse/STS 中的 Run as JUnit Testsrc/main/resources 中的 application.properties 将被拾取,因为 main class路径先于测试 class路径.

打开菜单栏即可查看 Run > Run Configurations > JUnit > *your_run_configuration* > 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;

有没有看到classpathxxx\main在前,然后是 xxx\test?对了,都是关于class路径 :-)

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

更改顺序:

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

只需转到 Project > Properties > Java Build Path > Order and Export,更改构建 class 路径顺序,将任何 test 文件夹放在首位,例如:

就是这样!

更好的解决方案

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

spring.profiles.active=test

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

如果您正在使用 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);
    }
}

我想你也可以用这个:

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

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

文件优先

详情请参考here

您可以在 src/test/resources/META-INF 中创建一个 spring.factories 文件,在 src/test/java 中创建一个 EnvironmentPostProcessor 实现 class。
spring.factories 喜欢

# 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);
    }
}