Если вы более или менее опытный пользователь Spring Boot , очень удачно, что в какой-то момент вам, возможно, придется столкнуться с ситуацией, когда конкретные бины или конфигурации должны быть введены условно . Механика этого хорошо понятна, но иногда тестирование таких условий (и их комбинаций) может привести к путанице. В этом посте мы поговорим о некоторых возможных (возможно, вменяемых) способах решения этой проблемы.
Поскольку Spring Boot 1.5.x по-прежнему широко используется (тем не менее он движется в направлении EOL в августе этого года ), мы бы включили его вместе с Spring Boot 2.1.x , как с JUnit 4.x, так и с JUnit 5.x. Методы, которые мы собираемся охватить, в равной степени применимы как к обычным классам конфигурации, так и к классам автоконфигурации .
Пример, с которым мы будем играть, будет связан с нашей самодельной регистрацией. Предположим, что нашему приложению Spring Boot требуется некоторый компонент для выделенного регистратора с именем «sample» . Однако в определенных обстоятельствах этот регистратор должен быть отключен (или фактически превращен в noop), поэтому свойство logging.enabled служит здесь как переключатель уничтожения. В этом примере мы используем Slf4j и Logback , но это не очень важно. Фрагмент LoggingConfiguration ниже отражает эту идею.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
@Configurationpublic class LoggingConfiguration { @Configuration @ConditionalOnProperty(name = "logging.enabled", matchIfMissing = true) public static class Slf4jConfiguration { @Bean Logger logger() { return LoggerFactory.getLogger("sample"); } } @Bean @ConditionalOnMissingBean Logger logger() { return new NOPLoggerFactory().getLogger("sample"); }} |
Так как бы мы это проверили? Spring Boot (и Spring Framework в целом) всегда предлагали выдающуюся поддержку лесов для тестирования . Аннотации @SpringBootTest и @TestPropertySource позволяют быстро загрузить контекст приложения с настроенными свойствами. Однако есть одна проблема: они применяются на уровне класса теста, а не на метод теста. Это, конечно, имеет смысл, но в основном требует создания тестового класса для каждой комбинации условий.
Если вы все еще работаете с JUnit 4.x , вам может пригодиться один трюк, который использует скрытый бегун, скрытую драгоценность фреймворка.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@RunWith(Enclosed.class)public class LoggingConfigurationTest { @RunWith(SpringRunner.class) @SpringBootTest public static class LoggerEnabledTest { @Autowired private Logger logger; @Test public void loggerShouldBeSlf4j() { assertThat(logger).isInstanceOf(ch.qos.logback.classic.Logger.class); } } @RunWith(SpringRunner.class) @SpringBootTest @TestPropertySource(properties = "logging.enabled=false") public static class LoggerDisabledTest { @Autowired private Logger logger; @Test public void loggerShouldBeNoop() { assertThat(logger).isSameAs(NOPLogger.NOP_LOGGER); } }} |
У вас все еще есть класс для каждого условия, но по крайней мере они все находятся в одном гнезде. С JUnit 5.x некоторые вещи стали проще, но не до уровня, который можно было бы ожидать. К сожалению, Spring Boot 1.5.x изначально не поддерживает JUnit 5.x , поэтому мы должны полагаться на расширение, предоставляемое модулем сообщества spring-test-junit5 . Вот соответствующие изменения в pom.xml , обратите внимание, что junit явно исключен из графика зависимостей spring-boot-starter-test .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions></dependency><dependency> <groupId>com.github.sbrannen</groupId> <artifactId>spring-test-junit5</artifactId> <version>1.5.0</version> <scope>test</scope></dependency><dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.5.0</version> <scope>test</scope></dependency><dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.5.0</version> <scope>test</scope></dependency> |
Сам контрольный пример не сильно отличается от использования аннотации @Nested , которая поставляется из JUnit 5.x для поддержки тестов как внутренних классов.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class LoggingConfigurationTest { @Nested @ExtendWith(SpringExtension.class) @SpringBootTest @DisplayName("Logging is enabled, expecting Slf4j logger") public static class LoggerEnabledTest { @Autowired private Logger logger; @Test public void loggerShouldBeSlf4j() { assertThat(logger).isInstanceOf(ch.qos.logback.classic.Logger.class); } } @Nested @ExtendWith(SpringExtension.class) @SpringBootTest @TestPropertySource(properties = "logging.enabled=false") @DisplayName("Logging is disabled, expecting NOOP logger") public static class LoggerDisabledTest { @Autowired private Logger logger; @Test public void loggerShouldBeNoop() { assertThat(logger).isSameAs(NOPLogger.NOP_LOGGER); } }} |
Если вы попытаетесь запустить тесты из командной строки с помощью плагинов Apache Maven и Maven Surefire , вы можете быть удивлены, увидев, что ни один из них не был выполнен во время сборки. Проблема в том, что … все вложенные классы исключены … поэтому нам нужно найти другой обходной путь .
|
01
02
03
04
05
06
07
08
09
10
|
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <excludes> <exclude /> </excludes> </configuration></plugin> |
При этом все должно идти гладко. Но хватит о наследии , Spring Boot 2.1.x полностью меняет игру. Семейство контекстных бегунов, ApplicationContextRunner , ReactiveWebApplicationContextRunner и WebApplicationContextRunner , предоставляют простой и прямой способ настройки контекста на уровне метода тестирования, поддерживая невероятно быстрое выполнение теста.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
public class LoggingConfigurationTest { private final ApplicationContextRunner runner = new ApplicationContextRunner() .withConfiguration(UserConfigurations.of(LoggingConfiguration.class)); @Test public void loggerShouldBeSlf4j() { runner .run(ctx -> assertThat(ctx.getBean(Logger.class)).isInstanceOf(Logger.class) ); } @Test public void loggerShouldBeNoop() { runner .withPropertyValues("logging.enabled=false") .run(ctx -> assertThat(ctx.getBean(Logger.class)).isSameAs(NOPLogger.NOP_LOGGER) ); }} |
Это выглядит действительно здорово. Поддержка JUnit 5.x в Spring Boot 2.1.x стала намного лучше, а с выходом 2.2 релиз, JUnit 5.x будет механизмом по умолчанию (не волнуйтесь, старый JUnit 4.x по- прежнему будет поддерживаться). На данный момент переход на JUnit 5.x требует немного работы на стороне зависимостей.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>junit</groupId> <artifactId>junit</artifactId> </exclusion> </exclusions></dependency><dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope></dependency><dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope></dependency> |
В качестве дополнительного шага вам может понадобиться использовать последний плагин Maven Surefire , версии 2.22.0 или выше, с готовой поддержкой JUnit 5.x. Фрагмент ниже иллюстрирует это.
|
1
2
3
4
5
|
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version></plugin> |
Пример конфигурации, с которым мы работали, довольно наивен, многие из реальных приложений заканчиваются довольно сложным контекстом, построенным из многих условий. Гибкость и огромные возможности, которые открывают раннеры контекста, бесценное дополнение к тестовым лесам Spring Boot 2.x , — просто живые заставки, имейте это в виду.
Полные источники проекта доступны на Github .
|
Опубликовано на Java Code Geeks с разрешения Андрея Редько, партнера нашей программы JCG . Посмотрите оригинальную статью здесь: Тестирование Spring Boot обусловливает здравый смысл Мнения, высказанные участниками Java Code Geeks, являются их собственными. |