Написание тестов @SpringBootTest при ипользовании Spring Shell в приложении

—

от автора

Иногда в приложениях полезно иметь консоль для управления приложением непосредственно с сервера. Одним из чрезвычайно удобных решений данной задачи является Spring Shell.

Тесты — тоже весьма неплохая практика (надеюсь у вас они есть) и, иногда, они пишутся с аннотацией @SpringBootTest. Однако, если вы подключите Spring Shell и попробуете запустить такой тест, то… ваш тест просто зависнет в ожидании введения команды с консоли.

Итак, отправляемся на поиски решения.

Гуглим

После недолгого поиска на GitHub находим похожую проблему.

Автор предлагает для тестирования shell-а переопределить бин с типом ApplicationRunner, который и ожидает команды с консоли. Здесь же решение по доступу и тестированию самих команд определенных в @ShellComponent.

@Component public class CliAppRunner implements ApplicationRunner { 	public CliAppRunner() {  	}  	@Override 	public void run(ApplicationArguments args) throws Exception { 		//do nothing 	} 	  }  @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes =CliConfig.class) public class ShellCommandIntegrationTest {  	@Autowired 	private Shell shell; 	 	@Test 	public void runTest(){ 		 		 		Object result=shell.evaluate(new Input(){ 			@Override 			public String rawText() { 				return "add 1 3"; 			} 			 		});  		DefaultResultHandler  resulthandler=new DefaultResultHandler(); 		resulthandler.handleResult(result); 		 		 	} 	 	 } 

К сожалению тесты при таком решении все равно зависают в ожидании команды.

Пришло время заглянуть под капот!

После легкого дебага находим в классе SpringApplication следующий код:

	private void callRunners(ApplicationContext context, ApplicationArguments args) { 		List<Object> runners = new ArrayList<>(); 		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); 		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); 		AnnotationAwareOrderComparator.sort(runners); 		for (Object runner : new LinkedHashSet<>(runners)) { 			if (runner instanceof ApplicationRunner) { 				callRunner((ApplicationRunner) runner, args); 			} 			if (runner instanceof CommandLineRunner) { 				callRunner((CommandLineRunner) runner, args); 			} 		} 	} 

Иначе говоря, Spring Boot просто добавляет наш бин с кастомным ApplicationRunner в копилку к уже определенным и запускает их все.

Казалось бы решение простое — переопределим бин! Время залезть в исходники Spring Shell.

Переопределяем бин

Быстро выясняется, что за создание раннеров отвечает класс JLineShellAutoConfiguration, конкретно нас интересует бин scriptApplicationRunner, который и не дает нашему тесту запуститься.

Ок, переопределим его в нашем тестовом классе (не забыв включить spring.main.allow-bean-definition-overriding=true для Spring 2.+):

	        @TestConfiguration 	static class Runner { 		@Bean 		public ApplicationRunner scriptApplicationRunner(){ 			return new CliAppRunner(); 		} 	} 

Нет, опять не сработало. JLineShellAutoConfiguration подгружается позже нашей тестовой конфигурации Runner и успешно переопределяет scriptApplicationRunner. И тест опять не запускается (Небольшой интерактив — кто-нибудь — объясните в комментариях, почему так?).

Ищем другие варианты

Что ж, посмотрим, что там написано в создании бина в JLineShellAutoConfiguration:

	@Bean 	@ConditionalOnProperty(prefix = SPRING_SHELL_SCRIPT, value = ScriptShellApplicationRunner.ENABLED, havingValue = "true", matchIfMissing = true) 	public ApplicationRunner scriptApplicationRunner(Parser parser, ConfigurableEnvironment environment) { 		return new ScriptShellApplicationRunner(parser, shell, environment); 	} 

Ура, нам повезло — есть property, который позволяет его отключить. Радостно бежим вписывать его в application.properties:

spring.shell.script.enabled=false 

Запускаем наш тест. И он опять зависает. Копаем дальше.

Разгадка

Идем в ScriptShellApplicationRunner и смотрим, что там с нашими property. А там:

	public static final String SPRING_SHELL_SCRIPT = "spring.shell.script"; 	public static final String ENABLED = "spring.shell.script";  	/** 	 * The name of the environment property that allows to disable the behavior of this 	 * runner. 	 */ 	public static final String SPRING_SHELL_SCRIPT_ENABLED = SPRING_SHELL_SCRIPT + "." + ENABLED; 

Воу, кажется теперь все понятно — идем снова в application.properties и пишем:

spring.shell.script.spring.shell.script=false  

Скрестим пальцы. Запускаем тест. Работает.

Дело раскрыто, спасибо за внимание.

ссылка на оригинал статьи https://habr.com/ru/post/502204/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *