diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java index 148c2f41e7f..3e0e48bf448 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java @@ -55,6 +55,7 @@ import org.springframework.test.context.web.WebMergedContextConfiguration; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; +import org.springframework.web.context.ConfigurableWebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext; /** @@ -103,14 +104,21 @@ public class SpringBootContextLoader extends AbstractContextLoader { } else if (config instanceof ReactiveWebMergedContextConfiguration) { application.setWebApplicationType(WebApplicationType.REACTIVE); - if (!isEmbeddedWebEnvironment(config)) { - application.setApplicationContextFactory( - ApplicationContextFactory.of(GenericReactiveWebApplicationContext::new)); - } } else { application.setWebApplicationType(WebApplicationType.NONE); } + application.setApplicationContextFactory((type) -> { + if (type != WebApplicationType.NONE && !isEmbeddedWebEnvironment(config)) { + if (type == WebApplicationType.REACTIVE) { + return new GenericReactiveWebApplicationContext(); + } + else if (type == WebApplicationType.SERVLET) { + return new GenericWebApplicationContext(); + } + } + return ApplicationContextFactory.DEFAULT.create(type); + }); application.setInitializers(initializers); ConfigurableEnvironment environment = getEnvironment(); if (environment != null) { @@ -261,14 +269,38 @@ public class SpringBootContextLoader extends AbstractContextLoader { List> initializers) { WebMergedContextConfiguration webConfiguration = (WebMergedContextConfiguration) configuration; addMockServletContext(initializers, webConfiguration); - application.setApplicationContextFactory((webApplicationType) -> new GenericWebApplicationContext()); } private void addMockServletContext(List> initializers, WebMergedContextConfiguration webConfiguration) { SpringBootMockServletContext servletContext = new SpringBootMockServletContext( webConfiguration.getResourceBasePath()); - initializers.add(0, new ServletContextApplicationContextInitializer(servletContext, true)); + initializers.add(0, new DefensiveWebApplicationContextInitializer( + new ServletContextApplicationContextInitializer(servletContext, true))); + } + + /** + * Decorator for {@link ServletContextApplicationContextInitializer} that prevents + * a failure when the context type is not as was predicted when the initializer + * was registered. This can occur when spring.main.web-application-type is set to + * something other than servlet. + */ + private static final class DefensiveWebApplicationContextInitializer + implements ApplicationContextInitializer { + + private final ServletContextApplicationContextInitializer delegate; + + private DefensiveWebApplicationContextInitializer(ServletContextApplicationContextInitializer delegate) { + this.delegate = delegate; + } + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + if (applicationContext instanceof ConfigurableWebApplicationContext) { + this.delegate.initialize((ConfigurableWebApplicationContext) applicationContext); + } + } + } } diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootContextLoaderTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootContextLoaderTests.java index ad03d67f4a0..3b9f4a5050b 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootContextLoaderTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootContextLoaderTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; @@ -36,6 +37,7 @@ import org.springframework.test.context.TestContextManager; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -146,6 +148,20 @@ class SpringBootContextLoaderTests { assertThat(last).startsWith("Config resource"); } + @Test + void whenEnvironmentChangesWebApplicationTypeToNoneThenContextTypeChangesAccordingly() { + TestContext context = new ExposedTestContextManager(ChangingWebApplicationTypeToNone.class) + .getExposedTestContext(); + assertThat(context.getApplicationContext()).isNotInstanceOf(WebApplicationContext.class); + } + + @Test + void whenEnvironmentChangesWebApplicationTypeToReactiveThenContextTypeChangesAccordingly() { + TestContext context = new ExposedTestContextManager(ChangingWebApplicationTypeToReactive.class) + .getExposedTestContext(); + assertThat(context.getApplicationContext()).isInstanceOf(GenericReactiveWebApplicationContext.class); + } + private String[] getActiveProfiles(Class testClass) { TestContext testContext = new ExposedTestContextManager(testClass).getExposedTestContext(); ApplicationContext applicationContext = testContext.getApplicationContext(); @@ -228,6 +244,16 @@ class SpringBootContextLoaderTests { } + @SpringBootTest(classes = Config.class, args = "--spring.main.web-application-type=none") + static class ChangingWebApplicationTypeToNone { + + } + + @SpringBootTest(classes = Config.class, args = "--spring.main.web-application-type=reactive") + static class ChangingWebApplicationTypeToReactive { + + } + /** * {@link TestContextManager} which exposes the {@link TestContext}. */