Voorkom dat ApplicationRunner of CommandLineRunner Beans worden uitgevoerd tijdens Junit-tests

1. Overzicht

In deze tutorial laten we zien hoe we bonen van het type kunnen voorkomen ToepassingRunner of CommandLineRunner wordt uitgevoerd tijdens Spring Boot-integratietests.

2. Voorbeeldtoepassing

Onze voorbeeldtoepassing bestaat uit een opdrachtregelprogramma, een toepassingsrunner en een taakserviceboon.

De commandoregeloperator roept het uitvoeren methode, om een ​​taak uit te voeren bij het opstarten van de applicatie:

@Component openbare klasse CommandLineTaskExecutor implementeert CommandLineRunner {privé TaskService taskService; openbare CommandLineTaskExecutor (TaskService taskService) {this.taskService = taskService; } @Override public void run (String ... args) gooit Uitzondering {taskService.execute ("command line runner task"); }} 

Op dezelfde manier werkt de applicatierunner samen met de taakservice om een ​​andere taak uit te voeren:

@Component openbare klasse ApplicationRunnerTaskExecutor implementeert ApplicationRunner {privé TaskService taskService; openbare ApplicationRunnerTaskExecutor (TaskService taskService) {this.taskService = taskService; } @Override public void run (ApplicationArguments args) genereert Uitzondering {taskService.execute ("application runner task"); }} 

Ten slotte is de taakdienst verantwoordelijk voor het uitvoeren van de taken van zijn cliënt:

@Service openbare klasse TaskService {privé statische Logger-logger = LoggerFactory.getLogger (TaskService.class); public void execute (String-taak) {logger.info ("do" + taak); }} 

En we hebben ook een Spring Boot-toepassingsklasse die ervoor zorgt dat alles werkt:

@SpringBootApplication openbare klasse ApplicationCommandLineRunnerApp {openbare statische leegte hoofd (String [] args) {SpringApplication.run (ApplicationCommandLineRunnerApp.class, args); }}

3. Verwacht gedrag testen

De ApplicationRunnerTaskExecutor en de CommandLineTaskExecutor uitvoeren nadat Spring Boot de toepassingscontext heeft geladen.

We kunnen dit verifiëren met een eenvoudige test:

Klasse @SpringBootTest RunApplicationIntegrationTest {@SpyBean ApplicationRunnerTaskExecutor applicationRunnerTaskExecutor; @SpyBean CommandLineTaskExecutor commandLineTaskExecutor; @Test ongeldig whenContextLoads_thenRunnersRun () gooit Uitzondering {verifieer (applicationRunnerTaskExecutor, keer (1)). Run (any ()); verifieer (commandLineTaskExecutor, times (1)). run (any ()); }}

Zoals we zien, gebruiken we de SpyBean annotatie voor het toepassen van Mockito-spionnen op het ApplicationRunnerTaskExecutor en CommandLineTaskExecutor bonen. Door dit te doen, kunnen we verifiëren dat het rennen methode van elk van deze bonen werd een keer genoemd.

In de volgende secties gaan we verschillende manieren en technieken bekijken om dit standaardgedrag te voorkomen tijdens onze Spring Boot-integratietests.

4. Preventie via veerprofielen

Een manier waarop we kunnen voorkomen dat deze twee worden uitgevoerd, is door ze te annoteren met @Profiel:

@Profile ("! Test") @Component openbare klasse CommandLineTaskExecutor implementeert CommandLineRunner {// hetzelfde als voorheen}
@Profile ("! Test") @Component openbare klasse ApplicationRunnerTaskExecutor implementeert ApplicationRunner {// hetzelfde als voorheen}

Na de bovenstaande wijzigingen gaan we verder met onze integratietest:

@ActiveProfiles ("test") @SpringBootTest-klasse RunApplicationWithTestProfileIntegrationTest {@Autowired privé ApplicationContext-context; @Test ongeldig whenContextLoads_thenRunnersAreNotLoaded () {assertNotNull (context.getBean (TaskService.class)); assertThrows (NoSuchBeanDefinitionException.class, () -> context.getBean (CommandLineTaskExecutor.class), "CommandLineRunner mag niet worden geladen tijdens deze integratietest"); assertThrows (NoSuchBeanDefinitionException.class, () -> context.getBean (ApplicationRunnerTaskExecutor.class), "ApplicationRunner mag niet worden geladen tijdens deze integratietest"); }}

Zoals we zien, we hebben de bovenstaande testklasse geannoteerd met de @ActiveProfiles ("test") annotatie, wat betekent dat het de annotaties met niet zal bedraden @Profile ("! Test"). Als gevolg hiervan is noch de CommandLineTaskExecutor boon noch de ApplicationRunnerTaskExecutor boon is helemaal geladen.

5. Preventie via de ConditionalOnProperty Annotatie

Of we kunnen hun bedrading per eigenschap configureren en vervolgens de ConditionalOnProperty annotatie:

@ConditionalOnProperty (prefix = "application.runner", value = "enabled", havingValue = "true", matchIfMissing = true) @Component openbare klasse ApplicationRunnerTaskExecutor implementeert ApplicationRunner {// hetzelfde als voorheen} 
@ConditionalOnProperty (prefix = "command.line.runner", value = "enabled", havingValue = "true", matchIfMissing = true) @Component openbare klasse CommandLineTaskExecutor implementeert CommandLineRunner {// hetzelfde als voorheen}

Zoals we zien, de ApplicationRunnerTaskExecutor en de CommandLineTaskExecutor zijn standaard ingeschakeld, en we kunnen ze uitschakelen als we de volgende eigenschappen instellen op false:

  • command.line.runner.enabled
  • application.runner.enabled

Dus, in onze test, we stellen deze eigenschappen in op false en ook niet de ApplicationRunnerTaskExecutor noch de CommandLineTaskExecutor bonen worden in de toepassingscontext geladen:

@SpringBootTest (properties = {"command.line.runner.enabled = false", "application.runner.enabled = false"}) class RunApplicationWithTestPropertiesIntegrationTest {// hetzelfde als voorheen}

Hoewel de bovenstaande technieken ons helpen ons doel te bereiken, zijn er gevallen waarin we willen testen of alle Spring beans correct zijn geladen en bedraad.

We willen bijvoorbeeld testen of de TaskService boon correct is geïnjecteerd in de CommandLineTaskExecutor, maar we willen het nog steeds niet rennen methode die tijdens onze test moet worden uitgevoerd. Laten we dus de laatste sectie bekijken waarin wordt uitgelegd hoe we dat kunnen bereiken.

6. Preventie door niet de hele container te bootstrappen

Hier zullen we beschrijven hoe we de CommandLineTaskExecutor en ApplicationRunnerTaskExecutor bonen uit de uitvoering door niet de hele applicatiecontainer te bootstrappen.

In de vorige secties hebben we de @BuienRadarNL annotatie en dit resulteerde in het opstarten van de volledige container tijdens onze integratietests. @BuienRadarNL bevat twee meta-annotaties die relevant zijn voor deze laatste oplossing:

@BootstrapWith (SpringBootTestContextBootstrapper.class) @ExtendWith (SpringExtension.class) 

Als het niet nodig is om de hele container op te starten tijdens onze test, wil je @BootstrapWith.

In plaats daarvan, we kunnen het vervangen door @ContextConfiguration:

@ContextConfiguration (classes = {ApplicationCommandLineRunnerApp.class}, initializers = ConfigFileApplicationContextInitializer.class)

Met @ContextConfiguration, we bepalen hoe de applicatiecontext voor integratietests moet worden geladen en geconfigureerd. Door de ContextConfiguration klassen eigenschap, verklaren we dat Spring Boot de ApplicationCommandLineRunnerApp class om de toepassingscontext te laden. Door de initialisatiefunctie te definiëren als de ConfigFileApplicationContextInitializer, laadt de applicatie zijn eigenschappen.

We hebben nog steeds nodig@ExtendWith (SpringExtension.class) omdat dat het Spring TestContext Framework integreert in het Jupiter-programmeermodel van JUnit 5.

Als resultaat van het bovenstaande, de Spring Boot-toepassingscontext laadt de componenten en eigenschappen van de toepassing zonder de CommandLineTaskExecutor of de ApplicationRunnerTaskExecutor bonen:

@ExtendWith (SpringExtension.class) @ContextConfiguration (klassen = {ApplicationCommandLineRunnerApp.class}, initializers = ConfigFileApplicationContextInitializer.class) openbare klasse LoadSpringContextIntegrationTest {@SpyBean TaskService taskService; @SpyBean CommandLineRunner commandLineRunner; @SpyBean ApplicationRunner applicationRunner; @Test ongeldig whenContextLoads_thenRunnersDoNotRun () gooit Uitzondering {assertNotNull (taskService); assertNotNull (commandLineRunner); assertNotNull (applicationRunner); verifieer (taskService, times (0)). execute (any ()); verifieer (commandLineRunner, times (0)). run (any ()); verifieer (applicationRunner, times (0)). run (any ()); }} 

Daar moeten we ook rekening mee houden de ConfigFileApplicationContextInitializer, als het alleen wordt gebruikt, biedt het geen ondersteuning voor @Value ("$ {…}") injectie. Als we het willen ondersteunen, moeten we een PropertySourcesPlaceholderConfigurer.

7. Conclusie

In dit artikel hebben we verschillende manieren laten zien om de uitvoering van het ToepassingRunner en CommandLineRunner bonen tijdens Spring Boot-integratietests.

Zoals altijd is de code beschikbaar op GitHub.