Переопределить стандартные настройки 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)
[ Обновление: переопределение определенных свойств для тестирования ]
- добавлять
src/main/resources/application-test.properties
, - Аннотировать тестовый класс с
@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.