Spring интеграционные тесты с профилем
В наших веб-приложениях Spring мы используем профили Spring bean для дифференциации трех сценариев: разработки, интеграции и производства. Мы используем их для подключения к различным базам данных или для установки других констант.
Использование профилей Spring bean очень хорошо работает для изменения среды веб-приложения.
Проблема заключается в том, что нам необходимо изменить код интеграции для среды. В этих случаях интеграционный тест загружает контекст приложения веб-приложения. Таким образом, нам не нужно переопределять соединения с базой данных, константы и т.д. (Применяя принцип DRY).
Мы устанавливаем наши интеграционные тесты следующим образом.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ["classpath:applicationContext.xml"])
public class MyTestIT
{
@Autowired
@Qualifier("myRemoteURL") // a value from the web-app applicationContext.xml
private String remoteURL;
...
}
Я могу запустить его локально с помощью @ActiveProfiles
, но это жестко закодировано и приводит к сбою наших тестов на сервере сборки.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = ["classpath:applicationContext.xml"])
@ActiveProfiles("development")
public class MyTestIT
{ ... }
Я также попытался использовать @WebAppConfiguration
, надеясь, что он каким-то образом может импортировать свойство spring.profiles.active
из Maven, но это не работает.
Еще одно замечание, нам также необходимо настроить наш код, чтобы разработчики могли запускать веб-приложение, а затем запускать тесты с помощью тестового бегуна IntelliJ (или другой среды IDE). Это намного проще для отладки тестов интеграции.
Ответы
Ответ 1
Как уже указывали другие люди, вы можете использовать Maven для установки системного свойства spring.profiles.active
, не допуская использования @ActiveProfiles
, но это не удобно для тестов, выполняемых в среде IDE.
Для программных средств для установки активных профилей у вас есть несколько вариантов.
- Spring 3.1: напишите пользовательский
ContextLoader
, который подготавливает контекст, задав активные профили в контексте Environment
.
- Spring 3.2: пользовательский
ContextLoader
остается опцией, но лучше выбрать ApplicationContextInitializer
и настроить его с помощью атрибута initializers
@ContextConfiguration
. Пользовательский инициализатор может настроить Environment
путем программной настройки активных профилей.
- Spring 4.0: вышеупомянутые варианты все еще существуют; однако с Spring Framework 4.0 для этой цели существует специальный выделенный
ActiveProfilesResolver
API: программно определить набор активных профилей для использования в тесте. ActiveProfilesResolver
можно зарегистрировать с помощью атрибута resolver
@ActiveProfiles
.
Привет,
Сэм (автор Spring TestContext Framework)
Ответ 2
У меня была аналогичная проблема: я хотел запустить все свои тесты интеграции с профилем по умолчанию, но разрешить пользователю переопределять профиль, представляющий другую среду или даже вкус db, без изменения значения @ActiveProfiles. Это можно сделать, если вы используете Spring 4.1+ с помощью специального ActiveProfilesResolver.
В этом примере resolver ищет системное свойство 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 { }
Конечно, вы можете использовать другие методы, кроме проверки наличия системного свойства для установки активных профилей. Надеюсь, это поможет кому-то.
Ответ 3
Если вы хотите избежать жесткого кодирования профиля, вы можете использовать системное свойство spring.profiles.active
и установить его на все, что вы необходимости в этой конкретной среде, например у нас есть профили "dev", "stage" и "prod" для разных условий; также у нас есть тестовые, тестовые и тестовые серверы для тестирования.
Помните, что вы можете иметь более одного профиля в этом системном свойстве, используя список разделенных запятыми значений, например. "Тест, тест-контроль качества".
Вы можете указать системные свойства в проекте maven в плагине maven surefire или передать их следующим образом:
mvn -DargLine="-DpropertyName=propertyValue"
Ответ 4
Как отметил @ElderMael, вы можете использовать свойство argLine плагина maven surefire. Часто, когда мне нужно запустить все тесты с различными конкретными профилями 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 in-db памяти, но иногда вы хотите запустить тест в "реальной" базе данных. Вы можете определить этот дополнительный профиль maven и определить работу Jenkins. С SpringBoot вы также можете добавить дополнительные свойства для проверки/ресурсов с помощью имени application-foo.yml(или свойств), и эти свойства будут учтены.
Ответ 5
есть много лиц для этой проблемы.
в моем случае, простое дополнение к build.gradle уже помогло:
test { systemProperties = System.properties }