diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index edae61c646a..72850b1c04e 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -148,6 +148,11 @@ spring-webmvc test + + org.springframework + spring-webflux + test + diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebMergedContextConfiguration.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebMergedContextConfiguration.java new file mode 100644 index 00000000000..d2c9b29c2e7 --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/ReactiveWebMergedContextConfiguration.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.test.context; + +import org.springframework.test.context.MergedContextConfiguration; + +/** + * Encapsulates the merged context configuration declared on a test class and + * all of its superclasses for a reactive web application. + * + * @author Stephane Nicoll + */ +class ReactiveWebMergedContextConfiguration extends MergedContextConfiguration { + + ReactiveWebMergedContextConfiguration( + MergedContextConfiguration mergedConfig) { + super(mergedConfig); + } +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java index 435a492bdf5..2958cff33ab 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java @@ -114,6 +114,9 @@ public class SpringBootContextLoader extends AbstractContextLoader { new WebConfigurer().configure(config, application, initializers); } } + else if (config instanceof ReactiveWebMergedContextConfiguration) { + application.setWebApplicationType(WebApplicationType.REACTIVE); + } else { application.setWebApplicationType(WebApplicationType.NONE); } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java index 0cb252a8503..111241f4cac 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -20,6 +20,7 @@ import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; @@ -27,9 +28,14 @@ import org.apache.commons.logging.LogFactory; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.WebApplicationType; +import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySources; +import org.springframework.core.env.PropertySourcesPropertyResolver; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextLoader; @@ -38,6 +44,7 @@ import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContextBootstrapper; import org.springframework.test.context.TestExecutionListener; import org.springframework.test.context.support.DefaultTestContextBootstrapper; +import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebMergedContextConfiguration; import org.springframework.util.Assert; @@ -146,7 +153,8 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr .toArray(new String[propertySourceProperties.size()])); WebEnvironment webEnvironment = getWebEnvironment(mergedConfig.getTestClass()); if (webEnvironment != null) { - if (deduceWebApplication() == WebApplicationType.SERVLET && + WebApplicationType webApplicationType = getWebApplicationType(mergedConfig); + if (webApplicationType == WebApplicationType.SERVLET && (webEnvironment.isEmbedded() || webEnvironment == WebEnvironment.MOCK)) { WebAppConfiguration webAppConfiguration = AnnotatedElementUtils .findMergedAnnotation(mergedConfig.getTestClass(), @@ -156,10 +164,24 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr mergedConfig = new WebMergedContextConfiguration(mergedConfig, resourceBasePath); } + else if (webApplicationType == WebApplicationType.REACTIVE + && webEnvironment.isEmbedded()) { + return new ReactiveWebMergedContextConfiguration(mergedConfig); + } } return mergedConfig; } + private WebApplicationType getWebApplicationType( + MergedContextConfiguration configuration) { + WebApplicationType webApplicationType = + getConfiguredWebApplicationType(configuration); + if (webApplicationType != null) { + return webApplicationType; + } + return deduceWebApplication(); + } + private WebApplicationType deduceWebApplication() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { @@ -173,6 +195,25 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr return WebApplicationType.SERVLET; } + private WebApplicationType getConfiguredWebApplicationType( + MergedContextConfiguration configuration) { + PropertySources sources = convertToPropertySources( + configuration.getPropertySourceProperties()); + RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( + new PropertySourcesPropertyResolver(sources), "spring.main."); + String property = resolver.getProperty("web-application-type"); + return (property != null ? WebApplicationType.valueOf(property.toUpperCase()) + : null); + } + + private PropertySources convertToPropertySources(String[] properties) { + Map source = TestPropertySourceUtils + .convertInlinedPropertiesToMap(properties); + MutablePropertySources sources = new MutablePropertySources(); + sources.addFirst(new MapPropertySource("inline", source)); + return sources; + } + protected Class[] getOrFindConfigurationClasses( MergedContextConfiguration mergedConfig) { Class[] classes = mergedConfig.getClasses(); diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests.java new file mode 100644 index 00000000000..8051efbd9dc --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.test.context; + +import org.junit.Test; +import reactor.core.publisher.Mono; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.context.embedded.ReactiveWebApplicationContext; +import org.springframework.boot.context.embedded.ReactiveWebServerFactory; +import org.springframework.boot.context.embedded.tomcat.TomcatReactiveWebServerFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.http.server.reactive.HttpHandler; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Base class for {@link SpringBootTest} tests configured to start an embedded reactive + * container. + * + * @author Stephane Nicoll + */ +public abstract class AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests { + + @LocalServerPort + private int port = 0; + + @Value("${value}") + private int value = 0; + + @Autowired + private ReactiveWebApplicationContext context; + + public ReactiveWebApplicationContext getContext() { + return this.context; + } + + @Test + public void runAndTestHttpEndpoint() { + assertThat(this.port).isNotEqualTo(8080).isNotEqualTo(0); + String body = new RestTemplate() + .getForObject("http://localhost:" + this.port + "/", String.class); + assertThat(body).isEqualTo("Hello World"); + } + + @Test + public void annotationAttributesOverridePropertiesFile() throws Exception { + assertThat(this.value).isEqualTo(123); + } + + protected static class AbstractConfig { + + @Value("${server.port:8080}") + private int port = 8080; + + @Bean + public HttpHandler httpHandler(ApplicationContext applicationContext) { + return WebHttpHandlerBuilder.applicationContext(applicationContext).build(); + } + + @Bean + public ReactiveWebServerFactory embeddedReactiveContainer() { + TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); + factory.setPort(this.port); + return factory; + } + + @Bean + public static PropertySourcesPlaceholderConfigurer propertyPlaceholder() { + return new PropertySourcesPlaceholderConfigurer(); + } + + @RequestMapping("/") + public Mono home() { + return Mono.just("Hello World"); + } + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedServletWebEnvironmentTests.java similarity index 96% rename from spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java rename to spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedServletWebEnvironmentTests.java index 9fe2c8d60be..1b9d881cd41 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedWebEnvironmentTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/AbstractSpringBootTestEmbeddedServletWebEnvironmentTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -43,7 +43,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Phillip Webb * @author Andy Wilkinson */ -public abstract class AbstractSpringBootTestEmbeddedWebEnvironmentTests { +public abstract class AbstractSpringBootTestEmbeddedServletWebEnvironmentTests { @LocalServerPort private int port = 0; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentDefinedPortTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentDefinedPortTests.java new file mode 100644 index 00000000000..1a62e8f1929 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentDefinedPortTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.test.context; + +import org.junit.runner.RunWith; + +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.config.EnableWebFlux; + +/** + * Tests for {@link SpringBootTest} in a reactive environment configured + * with {@link WebEnvironment#DEFINED_PORT}. + * + * @author Stephane Nicoll + */ +@RunWith(SpringRunner.class) +@DirtiesContext +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = { + "spring.main.web-application-type=reactive", "server.port=0", "value=123" }) +public class SpringBootTestReactiveWebEnvironmentDefinedPortTests + extends AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests { + + @Configuration + @EnableWebFlux + @RestController + protected static class Config extends AbstractConfig { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentRandomPortTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentRandomPortTests.java new file mode 100644 index 00000000000..a115ef51e2c --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestReactiveWebEnvironmentRandomPortTests.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2017 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 + * + * http://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.test.context; + +import org.junit.runner.RunWith; + +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.config.EnableWebFlux; + +/** + * Tests for {@link SpringBootTest} in a reactive environment configured + * with {@link WebEnvironment#RANDOM_PORT}. + * + * @author Stephane Nicoll + */ +@RunWith(SpringRunner.class) +@DirtiesContext +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { + "spring.main.webApplicationType=reactive", "value=123" }) +public class SpringBootTestReactiveWebEnvironmentRandomPortTests + extends AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests { + + @Configuration + @EnableWebFlux + @RestController + protected static class Config extends AbstractConfig { + + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java index 2887b6061ed..a1d53f849e1 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestTestRestTemplateDefinedByUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -40,7 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat; @DirtiesContext @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "value=123" }) public class SpringBootTestTestRestTemplateDefinedByUser - extends AbstractSpringBootTestEmbeddedWebEnvironmentTests { + extends AbstractSpringBootTestEmbeddedServletWebEnvironmentTests { @Test public void restTemplateIsUserDefined() throws Exception { diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentDefinedPortTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentDefinedPortTests.java index cb548bf9122..0581ff30629 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentDefinedPortTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentDefinedPortTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -36,7 +36,7 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = { "server.port=0", "value=123" }) public class SpringBootTestWebEnvironmentDefinedPortTests - extends AbstractSpringBootTestEmbeddedWebEnvironmentTests { + extends AbstractSpringBootTestEmbeddedServletWebEnvironmentTests { @Configuration @EnableWebMvc diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortCustomPortTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortCustomPortTests.java index e216dc8ab45..bffe5b7965f 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortCustomPortTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortCustomPortTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -20,7 +20,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.AbstractSpringBootTestEmbeddedWebEnvironmentTests.AbstractConfig; +import org.springframework.boot.test.context.AbstractSpringBootTestEmbeddedServletWebEnvironmentTests.AbstractConfig; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortTests.java index 516f33fc3f7..8df6e065b9d 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/context/SpringBootTestWebEnvironmentRandomPortTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat; @DirtiesContext @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "value=123" }) public class SpringBootTestWebEnvironmentRandomPortTests - extends AbstractSpringBootTestEmbeddedWebEnvironmentTests { + extends AbstractSpringBootTestEmbeddedServletWebEnvironmentTests { @Test public void testRestTemplateShouldUseBuilder() throws Exception {