Commit 54939e8e authored by Stephane Nicoll's avatar Stephane Nicoll Committed by Brian Clozel

Start a reactive web application if necessary

This commit makes sure that `@SpringBootTest` with a reactive setup
starts a web application if necessary.

If both a servlet and a reactive environment are available, a servlet
environment is bootstraped. This commit also adds a way to force a
reactive environment by specifying the `spring.main.web-application-type`
property of the test (e.g. `@TestPropertySource`).

Closes gh-8383
parent 8317977e
......@@ -148,6 +148,11 @@
<artifactId>spring-webmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
......
/*
* 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 <em>merged</em> 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);
}
}
......@@ -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);
}
......
/*
* 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<String, Object> 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();
......
/*
* 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<String> home() {
return Mono.just("Hello World");
}
}
}
/*
* 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;
......
/*
* 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 {
}
}
/*
* 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 {
}
}
/*
* 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 {
......
/*
* 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
......
/*
* 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;
......
/*
* 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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment