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 {