1. Обзор
В этом учебнике мы покажем, как мы можем предотвратить бобы типа ПриложениеРаннер или CommandLineRunner от запуска во время интеграционных тестов Spring Boot.
2. Пример применения
Наше примерное приложение состоит из бегуна командной строки, бегуна приложений и фасоли службы задач.
Бегущий по командной строке вызывает команду службы выполнить метод, для выполнения задачи по запуску приложения:
@Component public class CommandLineTaskExecutor implements CommandLineRunner { private TaskService taskService; public CommandLineTaskExecutor(TaskService taskService) { this.taskService = taskService; } @Override public void run(String... args) throws Exception { taskService.execute("command line runner task"); } }
Таким же образом бегун приложения взаимодействует со службой задач для выполнения другой задачи:
@Component public class ApplicationRunnerTaskExecutor implements ApplicationRunner { private TaskService taskService; public ApplicationRunnerTaskExecutor(TaskService taskService) { this.taskService = taskService; } @Override public void run(ApplicationArguments args) throws Exception { taskService.execute("application runner task"); } }
Наконец, служба задач отвечает за выполнение задач своего клиента:
@Service public class TaskService { private static Logger logger = LoggerFactory.getLogger(TaskService.class); public void execute(String task) { logger.info("do " + task); } }
И, у нас также есть класс приложения Spring Boot, что делает все это работает:
@SpringBootApplication public class ApplicationCommandLineRunnerApp { public static void main(String[] args) { SpringApplication.run(ApplicationCommandLineRunnerApp.class, args); } }
3. Тестирование ожидаемого поведения
тем ПриложениеRunnerTaskExecutor и CommandLineTaskExecutor вы запустите после того, как Spring Boot загружает контекст приложения.
Мы можем проверить это с помощью простого теста:
@SpringBootTest class RunApplicationIntegrationTest { @SpyBean ApplicationRunnerTaskExecutor applicationRunnerTaskExecutor; @SpyBean CommandLineTaskExecutor commandLineTaskExecutor; @Test void whenContextLoads_thenRunnersRun() throws Exception { verify(applicationRunnerTaskExecutor, times(1)).run(any()); verify(commandLineTaskExecutor, times(1)).run(any()); } }
Как мы видим, мы используем Шпионский аннотация для применения Мокито шпионов в ПриложениеRunnerTaskExecutor и CommandLineTaskExecutor фасоль. Поступая таким образом, мы можем проверить, что запустить метод каждой из этих бобов был назван один раз.
В следующих разделах мы увидим различные способы и методы предотвращения такого поведения по умолчанию во время наших интеграционных тестов Spring Boot.
4. Профилактика с помощью весенних профилей
Один из способов, который мы можем предотвратить эти два от запуска, аннотируя их @Profile :
@Profile("!test") @Component public class CommandLineTaskExecutor implements CommandLineRunner { // same as before }
@Profile("!test") @Component public class ApplicationRunnerTaskExecutor implements ApplicationRunner { // same as before }
После вышеуказанных изменений мы приступаем к нашему интеграционному тесту:
@ActiveProfiles("test") @SpringBootTest class RunApplicationWithTestProfileIntegrationTest { @Autowired private ApplicationContext context; @Test void whenContextLoads_thenRunnersAreNotLoaded() { assertNotNull(context.getBean(TaskService.class)); assertThrows(NoSuchBeanDefinitionException.class, () -> context.getBean(CommandLineTaskExecutor.class), "CommandLineRunner should not be loaded during this integration test"); assertThrows(NoSuchBeanDefinitionException.class, () -> context.getBean(ApplicationRunnerTaskExecutor.class), "ApplicationRunner should not be loaded during this integration test"); } }
Как мы видим, мы аннотировали вышеуказанный тестовый класс с @ActiveProfiles (“тест”) аннотация, что означает, что она не будет проволоки тех, аннотированных @Profile (“!тест”) . В результате ни один из CommandLineTaskExecutor фасоль, ни ПриложениеRunnerTaskExecutor фасоль загружается на всех.
5. Профилактика с помощью Аннотации УсловногоНаПропертии
Или мы можем настроить их проводку по свойству, а затем использовать аннотацию ConditionalOnProperty:
@ConditionalOnProperty( prefix = "application.runner", value = "enabled", havingValue = "true", matchIfMissing = true) @Component public class ApplicationRunnerTaskExecutor implements ApplicationRunner { // same as before }
@ConditionalOnProperty( prefix = "command.line.runner", value = "enabled", havingValue = "true", matchIfMissing = true) @Component public class CommandLineTaskExecutor implements CommandLineRunner { // same as before }
Как мы видим, ПриложениеRunnerTaskExecutor и CommandLineTaskExecutor включены по умолчанию, и мы можем отключить их, если мы установите следующие свойства, чтобы ложные :
- command.line.runner.enabled
- application.runner.enabled
Итак, в нашем тесте, мы устанавливаем эти свойства для ложные и ни ПриложениеRunnerTaskExecutor ни CommandLineTaskExecutor бобы загружаются в контекст приложения :
@SpringBootTest(properties = { "command.line.runner.enabled=false", "application.runner.enabled=false" }) class RunApplicationWithTestPropertiesIntegrationTest { // same as before }
Теперь, хотя вышеперечисленные методы помогают нам достичь нашей цели, Есть случаи, когда мы хотим проверить, что все весенние бобы загружаются и проводной правильно.
Например, мы можем проверить, что Служба задач фасоль вводится правильно в CommandLineTaskExecutor, но мы все еще не хотим его запустить метод, который будет выполнен во время нашего теста. Итак, давайте посмотрим последний раздел, который объясняет, как мы можем достичь этого.
6. Профилактика, не bootstrapping весь контейнер
Здесь мы опишем, как мы можем предотвратить CommandLineTaskExecutor и ПриложениеRunnerTaskExecutor бобы от исполнения, не bootstrapping весь контейнер приложения.
В предыдущих разделах мы использовали @SpringBootTest аннотация, и это привело к тому, что весь контейнер был загвеирован во время наших интеграционных тестов. @SpringBootTest включает в себя две мета-аннотации которые имеют отношение к этому последнему решению:
@BootstrapWith(SpringBootTestContextBootstrapper.class) @ExtendWith(SpringExtension.class)
Ну, если нет необходимости bootstrap весь контейнер во время нашего теста, то не хочу использовать его @BootstrapWith .
Вместо этого, мы можем заменить его @ContextConfiguration :
@ContextConfiguration(classes = {ApplicationCommandLineRunnerApp.class}, initializers = ConfigDataApplicationContextInitializer.class)
С @ContextConfiguration, мы определяем, как загрузить и настроить контекст приложения для интеграционных тестов. Установив КонтекстКонфигурация классы собственности, мы заявляем, что весенняя загрузка должна использовать ПриложениеCommandLineRunnerApp класса для загрузки контекста приложения. Определяя инициализатор, чтобы быть ConfigDataПрименениеКонтекстИнициализатор , приложение загружает свои свойства .
Нам все еще @ExtendWith (SpringExtension.class) так как это интегрирует Spring TestContext Framework в модель программирования JUnit 5.
В результате вышесказанного, контекст приложения Spring Boot загружает компоненты и свойства приложения без выполнения CommandLineTaskExecutor или ПриложениеRunnerTaskExecutor фасоль:
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { ApplicationCommandLineRunnerApp.class }, initializers = ConfigDataApplicationContextInitializer.class) public class LoadSpringContextIntegrationTest { @SpyBean TaskService taskService; @SpyBean CommandLineRunner commandLineRunner; @SpyBean ApplicationRunner applicationRunner; @Test void whenContextLoads_thenRunnersDoNotRun() throws Exception { assertNotNull(taskService); assertNotNull(commandLineRunner); assertNotNull(applicationRunner); verify(taskService, times(0)).execute(any()); verify(commandLineRunner, times(0)).run(any()); verify(applicationRunner, times(0)).run(any()); } }
Кроме того, мы должны иметь в виду, что ConfigDataПрименениеКонтекстИнициализатор , когда он используется в одиночку, не обеспечивает поддержку @Value (“$”…”) инъекция. Если мы хотим поддержать его, мы должны настроить НедвижимостьРесурсПлейсхолдерКонфигуратор .
7. Заключение
В этой статье мы показали различные способы предотвращения исполнения ПриложениеРаннер и CommandLineRunner бобов во время интеграционных тестов Spring Boot.
Как всегда, код доступен более на GitHub .