From 3630a43ed7b0a8b0706b633f838798cb9e43ec83 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 8 May 2025 11:50:47 +0100 Subject: [PATCH] Integration test each servlet web server with Spring MVC See gh-46071 --- .../spring-boot-jetty/build.gradle | 1 + ...tyServletWebServerMvcIntegrationTests.java | 45 +++++++++ .../spring-boot-tomcat/build.gradle | 1 + ...atServletWebServerMvcIntegrationTests.java | 45 +++++++++ .../spring-boot-undertow/build.gradle | 1 + ...owServletWebServerMvcIntegrationTests.java | 45 +++++++++ .../spring-boot-web-server/build.gradle | 1 + ...tServletWebServerMvcIntegrationTests.java} | 98 +++++-------------- 8 files changed, 166 insertions(+), 71 deletions(-) create mode 100644 spring-boot-project/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerMvcIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerMvcIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-undertow/src/test/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerMvcIntegrationTests.java rename spring-boot-project/{spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java => spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/servlet/context/AbstractServletWebServerMvcIntegrationTests.java} (65%) diff --git a/spring-boot-project/spring-boot-jetty/build.gradle b/spring-boot-project/spring-boot-jetty/build.gradle index 45f8ad8b642..d5557d56042 100644 --- a/spring-boot-project/spring-boot-jetty/build.gradle +++ b/spring-boot-project/spring-boot-jetty/build.gradle @@ -50,6 +50,7 @@ dependencies { testRuntimeOnly("org.eclipse.jetty:jetty-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client-transport") + testRuntimeOnly("org.springframework:spring-webmvc") } test { diff --git a/spring-boot-project/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerMvcIntegrationTests.java b/spring-boot-project/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerMvcIntegrationTests.java new file mode 100644 index 00000000000..92d00528384 --- /dev/null +++ b/spring-boot-project/spring-boot-jetty/src/test/java/org/springframework/boot/jetty/servlet/JettyServletWebServerMvcIntegrationTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.jetty.servlet; + +import org.springframework.boot.jetty.JettyWebServer; +import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.AbstractServletWebServerMvcIntegrationTests; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Integration tests for {@link ServletWebServerApplicationContext} and + * {@link JettyWebServer} running Spring MVC. + */ +class JettyServletWebServerMvcIntegrationTests extends AbstractServletWebServerMvcIntegrationTests { + + protected JettyServletWebServerMvcIntegrationTests() { + super(JettyConfig.class); + } + + @Configuration(proxyBeanMethods = false) + static class JettyConfig { + + @Bean + JettyServletWebServerFactory webServerFactory() { + return new JettyServletWebServerFactory(0); + } + + } + +} diff --git a/spring-boot-project/spring-boot-tomcat/build.gradle b/spring-boot-project/spring-boot-tomcat/build.gradle index 098f61ad7ac..10fa6e52e94 100644 --- a/spring-boot-project/spring-boot-tomcat/build.gradle +++ b/spring-boot-project/spring-boot-tomcat/build.gradle @@ -58,6 +58,7 @@ dependencies { testRuntimeOnly("org.eclipse.jetty:jetty-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client-transport") + testRuntimeOnly("org.springframework:spring-webmvc") tomcatDistribution("org.apache.tomcat:tomcat:${tomcatVersion}@zip") } diff --git a/spring-boot-project/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerMvcIntegrationTests.java b/spring-boot-project/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerMvcIntegrationTests.java new file mode 100644 index 00000000000..a3b95cf167b --- /dev/null +++ b/spring-boot-project/spring-boot-tomcat/src/test/java/org/springframework/boot/tomcat/servlet/TomcatServletWebServerMvcIntegrationTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.tomcat.servlet; + +import org.springframework.boot.tomcat.TomcatWebServer; +import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.AbstractServletWebServerMvcIntegrationTests; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Integration tests for {@link ServletWebServerApplicationContext} and + * {@link TomcatWebServer} running Spring MVC. + */ +class TomcatServletWebServerMvcIntegrationTests extends AbstractServletWebServerMvcIntegrationTests { + + protected TomcatServletWebServerMvcIntegrationTests() { + super(TomcatConfig.class); + } + + @Configuration(proxyBeanMethods = false) + static class TomcatConfig { + + @Bean + TomcatServletWebServerFactory webServerFactory() { + return new TomcatServletWebServerFactory(0); + } + + } + +} diff --git a/spring-boot-project/spring-boot-undertow/build.gradle b/spring-boot-project/spring-boot-undertow/build.gradle index b0f3472d56a..ff60d898e95 100644 --- a/spring-boot-project/spring-boot-undertow/build.gradle +++ b/spring-boot-project/spring-boot-undertow/build.gradle @@ -46,6 +46,7 @@ dependencies { testRuntimeOnly("org.eclipse.jetty:jetty-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client") testRuntimeOnly("org.eclipse.jetty.http2:jetty-http2-client-transport") + testRuntimeOnly("org.springframework:spring-webmvc") } test { diff --git a/spring-boot-project/spring-boot-undertow/src/test/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerMvcIntegrationTests.java b/spring-boot-project/spring-boot-undertow/src/test/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerMvcIntegrationTests.java new file mode 100644 index 00000000000..aca7f4dc54e --- /dev/null +++ b/spring-boot-project/spring-boot-undertow/src/test/java/org/springframework/boot/undertow/servlet/UndertowServletWebServerMvcIntegrationTests.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.undertow.servlet; + +import org.springframework.boot.undertow.UndertowWebServer; +import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.context.AbstractServletWebServerMvcIntegrationTests; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Integration tests for {@link ServletWebServerApplicationContext} and + * {@link UndertowWebServer} running Spring MVC. + */ +class UndertowServletWebServerMvcIntegrationTests extends AbstractServletWebServerMvcIntegrationTests { + + protected UndertowServletWebServerMvcIntegrationTests() { + super(UndertowConfig.class); + } + + @Configuration(proxyBeanMethods = false) + static class UndertowConfig { + + @Bean + UndertowServletWebServerFactory webServerFactory() { + return new UndertowServletWebServerFactory(0); + } + + } + +} diff --git a/spring-boot-project/spring-boot-web-server/build.gradle b/spring-boot-project/spring-boot-web-server/build.gradle index 899954de7d0..f104f9a10f6 100644 --- a/spring-boot-project/spring-boot-web-server/build.gradle +++ b/spring-boot-project/spring-boot-web-server/build.gradle @@ -48,6 +48,7 @@ dependencies { testFixturesCompileOnly("org.mockito:mockito-core") testFixturesCompileOnly("org.springframework:spring-tx") testFixturesCompileOnly("org.springframework:spring-webflux") + testFixturesCompileOnly("org.springframework:spring-webmvc") testImplementation(project(":spring-boot-project:spring-boot-test")) testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support")) diff --git a/spring-boot-project/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java b/spring-boot-project/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/servlet/context/AbstractServletWebServerMvcIntegrationTests.java similarity index 65% rename from spring-boot-project/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java rename to spring-boot-project/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/servlet/context/AbstractServletWebServerMvcIntegrationTests.java index 9f897638789..3877babf474 100644 --- a/spring-boot-project/spring-boot-integration-tests/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java +++ b/spring-boot-project/spring-boot-web-server/src/testFixtures/java/org/springframework/boot/web/servlet/context/AbstractServletWebServerMvcIntegrationTests.java @@ -21,19 +21,16 @@ import java.net.URI; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.jetty.servlet.JettyServletWebServerFactory; import org.springframework.boot.testsupport.classpath.resources.WithResource; -import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory; -import org.springframework.boot.undertow.servlet.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.server.servlet.ServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; +import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory; import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.PropertySource; import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod; @@ -49,17 +46,22 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import static org.assertj.core.api.Assertions.assertThat; /** - * Integration tests for {@link ServletWebServerApplicationContext} and {@link WebServer}s - * running Spring MVC. + * Base class for integration testing of {@link ServletWebServerApplicationContext} and + * {@link WebServer}s running Spring MVC. * * @author Phillip Webb * @author Ivan Sopov */ -@DirtiesUrlFactories -class ServletWebServerMvcIntegrationTests { +public abstract class AbstractServletWebServerMvcIntegrationTests { private AnnotationConfigServletWebServerApplicationContext context; + private Class webServerConfiguration; + + protected AbstractServletWebServerMvcIntegrationTests(Class webServerConfiguration) { + this.webServerConfiguration = webServerConfiguration; + } + @AfterEach void closeContext() { try { @@ -71,27 +73,17 @@ class ServletWebServerMvcIntegrationTests { } @Test - void tomcat() throws Exception { - this.context = new AnnotationConfigServletWebServerApplicationContext(TomcatConfig.class); - doTest(this.context, "/hello"); - } - - @Test - void jetty() throws Exception { - this.context = new AnnotationConfigServletWebServerApplicationContext(JettyConfig.class); - doTest(this.context, "/hello"); - } - - @Test - void undertow() throws Exception { - this.context = new AnnotationConfigServletWebServerApplicationContext(UndertowConfig.class); + void basicConfig() throws Exception { + this.context = new AnnotationConfigServletWebServerApplicationContext(this.webServerConfiguration, + Config.class); doTest(this.context, "/hello"); } @Test @WithResource(name = "conf.properties", content = "context=/example") void advancedConfig() throws Exception { - this.context = new AnnotationConfigServletWebServerApplicationContext(AdvancedConfig.class); + this.context = new AnnotationConfigServletWebServerApplicationContext(this.webServerConfiguration, + AdvancedConfig.class); doTest(this.context, "/example/spring/hello"); } @@ -105,45 +97,6 @@ class ServletWebServerMvcIntegrationTests { } } - // Simple main method for testing in a browser - @SuppressWarnings("resource") - static void main(String[] args) { - new AnnotationConfigServletWebServerApplicationContext(JettyServletWebServerFactory.class, Config.class); - } - - @Configuration(proxyBeanMethods = false) - @Import(Config.class) - static class TomcatConfig { - - @Bean - ServletWebServerFactory webServerFactory() { - return new TomcatServletWebServerFactory(0); - } - - } - - @Configuration(proxyBeanMethods = false) - @Import(Config.class) - static class JettyConfig { - - @Bean - ServletWebServerFactory webServerFactory() { - return new JettyServletWebServerFactory(0); - } - - } - - @Configuration(proxyBeanMethods = false) - @Import(Config.class) - static class UndertowConfig { - - @Bean - ServletWebServerFactory webServerFactory() { - return new UndertowServletWebServerFactory(0); - } - - } - @Configuration(proxyBeanMethods = false) @EnableWebMvc static class Config { @@ -151,9 +104,6 @@ class ServletWebServerMvcIntegrationTests { @Bean DispatcherServlet dispatcherServlet() { return new DispatcherServlet(); - // Alternatively you can use ServletContextInitializer beans including - // ServletRegistration and FilterRegistration. Read the - // EmbeddedWebApplicationContext Javadoc for details. } @Bean @@ -175,10 +125,16 @@ class ServletWebServerMvcIntegrationTests { } @Bean - ServletWebServerFactory webServerFactory() { - JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); - factory.setContextPath(this.env.getProperty("context")); - return factory; + static WebServerFactoryCustomizerBeanPostProcessor webServerFactoryCustomizerBeanPostProcessor() { + return new WebServerFactoryCustomizerBeanPostProcessor(); + } + + @Bean + WebServerFactoryCustomizer contextPathCustomizer() { + return (factory) -> { + String contextPath = this.env.getProperty("context"); + factory.setContextPath(contextPath); + }; } @Bean