Переопределить стандартные настройки Spring.boot application.properties в тесте Junit

У меня есть приложение Spring-Boot, где свойства по умолчанию установлены в application.properties файл в пути к классам (src/main/resources/application.properties).

Я хотел бы переопределить некоторые настройки по умолчанию в моем тесте JUnit со свойствами, объявленными в test.properties файл (src / test / resources / test.properties)

У меня обычно есть специальный класс Config для моих тестов 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 {

}

Я сначала подумал, что с помощью @PropertySource("classpath:test.properties") в классе TestConfig все получится, но эти свойства не будут перезаписывать настройки application.properties (см. Справочный документ Spring-Boot - 23. Внешняя конфигурация).

Тогда я попытался использовать -Dspring.config.location=classpath:test.properties при вызове теста. Это было успешно, но я не хочу устанавливать это системное свойство для каждого выполнения теста. Таким образом я положил это в коде

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

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

}

что, к сожалению, снова не удалось.

Должно быть простое решение о том, как переопределить application.properties настройки в тестах JUnit с test.properties что я, должно быть, упустил из виду.

15 ответов

Решение

Ты можешь использовать @TestPropertySource переопределить значения в application.properties, Из своего Javadoc:

источники свойств теста могут использоваться для выборочного переопределения свойств, определенных в источниках свойств системы и приложения

Например:

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

}

Spring Boot автоматически загружается src/test/resources/application.properties, если используются следующие аннотации

@RunWith(SpringRunner.class)
@SpringBootTest

Итак, переименуйте test.properties в application.properties использовать автоматическую настройку.

Если вам * нужно * только загрузить файл свойств (в среду), используйте также следующее, как описано здесь

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

[ Обновление: переопределение определенных свойств для тестирования ]

  1. добавлять src/main/resources/application-test.properties,
  2. Аннотировать тестовый класс с @ActiveProfiles("test"),

Это загружает application.properties а потом application-test.properties свойства в контексте приложения для тестового примера согласно правилам, определенным здесь.

Демонстрация - https://github.com/mohnish82/so-spring-boot-testprops

Вы также можете использовать метааннотации для вывода конфигурации. Например:

@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 { }

Другой подход, подходящий для переопределения нескольких свойств в вашем тесте, если вы используете @SpringBootTest аннотация:

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

Простое объяснение:

Если ты такой же, как я, и у тебя то же самое application.properties в src/main/resources а также src/test/resources, и вам интересно, почемуapplication.propertiesв тесте папка не перекрываяapplication.properties в ваших основных ресурсах читайте дальше...

Если у вас есть application.properties под src/main/resources и то же самое application.properties под src/test/resources, который application.propertiesподхватывается, зависит от того, как вы проводите свои тесты. Папка структура src/main/resources а также src/test/resources, является архитектурным соглашением Maven, поэтому, если вы запустите свой тест как mvnw test или даже gradlew test, то application.properties в src/test/resourcesбудет выбрано, так как тестовый путь к классам будет предшествовать основному пути к классам. Но если вы запустите свой тест какRun as JUnit Test в Elipse/STS application.properties в src/main/resourcesбудет выбрано, поскольку основной путь к классам предшествует пути к тестовым классам.

Вы можете проверить это, открыв 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\ имя_проекта \bin\main; XXX\ рабочее пространство-весна-инструмент-набор-4-4.5.1.RELEASE\ имя_проекта \bin\test;

Вы видите, что сначала идет \ main, а затем \test? Правильно, все дело в пути к классам:-)

Ура

TLDR:

Итак, что было сделано, чтобы иметь стандарт src/main/resources/application.properties а также src/test/resources/application-default.properties где я переопределяю некоторые настройки для ВСЕХ моих тестов.

Вся история

Я столкнулся с той же проблемой и до сих пор не использовал профили. Казалось, надоело делать это сейчас и помнить объявление профиля - о котором можно легко забыть.

Хитрость заключается в том, чтобы использовать этот профиль application-<profile>.properties переопределяет настройки в общем профиле. см. https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Если вы используете 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, они используются в дополнение к расположениям по умолчанию.

Файл будет иметь приоритет

Пожалуйста, обратитесь сюда для получения более подробной информации.

Это работает для меня:

Мой тест:

      @SpringBootTest
@TestPropertySource(properties = "spring.config.additional-location=classpath:application-test.yml")
class EngineApplicationTests {
    @Test
    void contextLoads() {
    }
}

Мои версии:

      plugins {
    id 'org.springframework.boot' version '2.7.1'
    id 'io.spring.dependency-management' version '1.0.12.RELEASE'
    id 'java'
}

group = 'com.kubemachine'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "2021.0.3")
}

Единственная тестовая зависимость в моем файле gradle:

      testImplementation 'org.springframework.boot:spring-boot-starter-test'

У меня также есть это в моем файле build.gradle:

      test {
    useJUnitPlatform()
}

и два файла свойств:

  • src/main/resources/application.yml
  • src/test/resources/application-test.yml

И в этой настройке application-test.yml определенно ТОЛЬКО переопределяет значения в application.yml. Мне не нужно повторять значения свойств из application.yml в application-test.yml. application-test.yml действительно расширяет application.yml.

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`

С использованием

  • свойства, объявленные с окончанием файла
  • Весенняя загрузка 2.7
  • и несколько файлов .yaml в пути к классам

Я заметил, что приоритет@TestPropertySource(locations)не применялся, как это было бы с.propertiesфайлы.

Проблема, с которой я столкнулся, заключалась в том, что Spring продолжал загружать все.yamlсвойства (особенно из производства) и переопределение свойств, предназначенных для тестов, значениями, указанными для prod. ( вздох )

Придумываем обходной путь перезаписи механизма очистки конфига, указав только мойapplication-test.yamlкак единственные свойства, используемые так:

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

В противном случае мы можем изменить имя конфигуратора свойств по умолчанию, установив свойство spring.config.name=test а затем имея ресурс пути к классу src/test/test.properties наш родной экземпляр org.springframework.boot.SpringApplication будет автоматически настроен из этого разделенного test.properties, игнорируя свойства приложения;

Преимущество: автоконфигурация тестов;

Недостаток: предоставление свойства "spring.config.name" на уровне CI

ссылка: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

spring.config.name = application # Имя файла конфигурации

Вы можете создать файл spring.factories в src/test/resources/META-INF и класс реализации EnvironmentPostProcessor в src/test/java.
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);
    }
}

Если вы действительно хотите/должны использоватьSystem.setProperties(...)затем используйте расширение Junit и аннотируйте тестовый класс:

      ...
@ExtendWith(MyBeforeAllCallback.class)
public SomeTestClass ...

и используйте конструктор обратного вызова для установки свойств системы.

      public MyBeforeAllCallback implements BeforeAllCallback {
    public MyBeforeAllCallback() {
        // wicket 9 workaround for java 17 / cglib issue.
        System.setProperty("wicket.ioc.useByteBuddy", "true");
        // ... whatever you want ...
        System.setProperty("whatever", "you need it badly");
    }
    void beforeAll(ExtensionContext context) {
        // ... empty
    }
}

Вы также можете создать файл application.properties в src/test/resources, где написаны ваши JUnits.

Другие вопросы по тегам