Skip to content

Commit c8de843

Browse files
committed
Honor final web application type when creating context in tests
Previously, SpringBootContextLoader would configure its SpringApplication with an ApplicationContextFactory that ignores the WebApplicationType with which its called and instead returns a hard-coded type of context based on the type of the MergedContextConfiguration. This hard-coding would result in the wrong type of context being used if a subsequent change was made to the application's WebApplicationType, for example due to binding of the spring.main.web-application-type configuration property. This commit updates SpringBootContextLoader to configure SpringApplication with an ApplicationContextFactory that takes the WebApplicationType with which it is called into consideration. Fixes gh-29170
1 parent dfc0f21 commit c8de843

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
import org.springframework.util.ObjectUtils;
6363
import org.springframework.util.ReflectionUtils;
6464
import org.springframework.util.StringUtils;
65+
import org.springframework.web.context.ConfigurableWebApplicationContext;
6566
import org.springframework.web.context.support.GenericWebApplicationContext;
6667

6768
/**
@@ -113,14 +114,21 @@ public ApplicationContext loadContext(MergedContextConfiguration config) throws
113114
}
114115
else if (config instanceof ReactiveWebMergedContextConfiguration) {
115116
application.setWebApplicationType(WebApplicationType.REACTIVE);
116-
if (!isEmbeddedWebEnvironment(config)) {
117-
application.setApplicationContextFactory(
118-
ApplicationContextFactory.of(GenericReactiveWebApplicationContext::new));
119-
}
120117
}
121118
else {
122119
application.setWebApplicationType(WebApplicationType.NONE);
123120
}
121+
application.setApplicationContextFactory((type) -> {
122+
if (type != WebApplicationType.NONE && !isEmbeddedWebEnvironment(config)) {
123+
if (type == WebApplicationType.REACTIVE) {
124+
return new GenericReactiveWebApplicationContext();
125+
}
126+
else if (type == WebApplicationType.SERVLET) {
127+
return new GenericWebApplicationContext();
128+
}
129+
}
130+
return ApplicationContextFactory.DEFAULT.create(type);
131+
});
124132
application.setInitializers(initializers);
125133
boolean customEnvironent = ReflectionUtils.findMethod(getClass(), "getEnvironment")
126134
.getDeclaringClass() != SpringBootContextLoader.class;
@@ -285,14 +293,38 @@ void configure(MergedContextConfiguration configuration, SpringApplication appli
285293
List<ApplicationContextInitializer<?>> initializers) {
286294
WebMergedContextConfiguration webConfiguration = (WebMergedContextConfiguration) configuration;
287295
addMockServletContext(initializers, webConfiguration);
288-
application.setApplicationContextFactory((webApplicationType) -> new GenericWebApplicationContext());
289296
}
290297

291298
private void addMockServletContext(List<ApplicationContextInitializer<?>> initializers,
292299
WebMergedContextConfiguration webConfiguration) {
293300
SpringBootMockServletContext servletContext = new SpringBootMockServletContext(
294301
webConfiguration.getResourceBasePath());
295-
initializers.add(0, new ServletContextApplicationContextInitializer(servletContext, true));
302+
initializers.add(0, new DefensiveWebApplicationContextInitializer(
303+
new ServletContextApplicationContextInitializer(servletContext, true)));
304+
}
305+
306+
/**
307+
* Decorator for {@link ServletContextApplicationContextInitializer} that prevents
308+
* a failure when the context type is not as was predicted when the initializer
309+
* was registered. This can occur when spring.main.web-application-type is set to
310+
* something other than servlet.
311+
*/
312+
private static final class DefensiveWebApplicationContextInitializer
313+
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
314+
315+
private final ServletContextApplicationContextInitializer delegate;
316+
317+
private DefensiveWebApplicationContextInitializer(ServletContextApplicationContextInitializer delegate) {
318+
this.delegate = delegate;
319+
}
320+
321+
@Override
322+
public void initialize(ConfigurableApplicationContext applicationContext) {
323+
if (applicationContext instanceof ConfigurableWebApplicationContext) {
324+
this.delegate.initialize((ConfigurableWebApplicationContext) applicationContext);
325+
}
326+
}
327+
296328
}
297329

298330
}

spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootContextLoaderTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.junit.jupiter.api.Test;
2525

2626
import org.springframework.boot.test.util.TestPropertyValues;
27+
import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext;
2728
import org.springframework.context.ApplicationContext;
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.core.env.ConfigurableEnvironment;
@@ -36,6 +37,7 @@
3637
import org.springframework.test.context.TestPropertySource;
3738
import org.springframework.test.context.support.TestPropertySourceUtils;
3839
import org.springframework.test.util.ReflectionTestUtils;
40+
import org.springframework.web.context.WebApplicationContext;
3941

4042
import static org.assertj.core.api.Assertions.assertThat;
4143

@@ -146,6 +148,20 @@ void propertySourceOrdering() throws Exception {
146148
assertThat(last).startsWith("Config resource");
147149
}
148150

151+
@Test
152+
void whenEnvironmentChangesWebApplicationTypeToNoneThenContextTypeChangesAccordingly() {
153+
TestContext context = new ExposedTestContextManager(ChangingWebApplicationTypeToNone.class)
154+
.getExposedTestContext();
155+
assertThat(context.getApplicationContext()).isNotInstanceOf(WebApplicationContext.class);
156+
}
157+
158+
@Test
159+
void whenEnvironmentChangesWebApplicationTypeToReactiveThenContextTypeChangesAccordingly() {
160+
TestContext context = new ExposedTestContextManager(ChangingWebApplicationTypeToReactive.class)
161+
.getExposedTestContext();
162+
assertThat(context.getApplicationContext()).isInstanceOf(GenericReactiveWebApplicationContext.class);
163+
}
164+
149165
private String[] getActiveProfiles(Class<?> testClass) {
150166
TestContext testContext = new ExposedTestContextManager(testClass).getExposedTestContext();
151167
ApplicationContext applicationContext = testContext.getApplicationContext();
@@ -228,6 +244,16 @@ static class Config {
228244

229245
}
230246

247+
@SpringBootTest(classes = Config.class, args = "--spring.main.web-application-type=none")
248+
static class ChangingWebApplicationTypeToNone {
249+
250+
}
251+
252+
@SpringBootTest(classes = Config.class, args = "--spring.main.web-application-type=reactive")
253+
static class ChangingWebApplicationTypeToReactive {
254+
255+
}
256+
231257
/**
232258
* {@link TestContextManager} which exposes the {@link TestContext}.
233259
*/

0 commit comments

Comments
 (0)