diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java
index 66701d2b1a..736c439ec1 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2018 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.
@@ -57,8 +57,9 @@ import org.springframework.web.context.WebApplicationContext;
* {@link WebEnvironment#DEFINED_PORT defined} or {@link WebEnvironment#RANDOM_PORT
* random} port.
*
Registers a {@link org.springframework.boot.test.web.client.TestRestTemplate
- * TestRestTemplate} bean for use in web tests that are using a fully running web server.
- *
+ * TestRestTemplate} and/or
+ * {@link org.springframework.test.web.reactive.server.WebTestClient WebTestClient} bean
+ * for use in web tests that are using a fully running web server.
*
*
* @author Phillip Webb
diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java
similarity index 71%
rename from spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java
rename to spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java
index 3279677dea..a7550cec26 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizer.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2018 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.
@@ -14,15 +14,21 @@
* limitations under the License.
*/
-package org.springframework.boot.test.web.reactive;
+package org.springframework.boot.test.web.reactive.server;
import java.util.Collection;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.codec.CodecCustomizer;
@@ -30,6 +36,8 @@ import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFac
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ConfigurationClassPostProcessor;
+import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
@@ -63,8 +71,11 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
private void registerWebTestClient(ConfigurableApplicationContext context,
BeanDefinitionRegistry registry) {
- registry.registerBeanDefinition(WebTestClient.class.getName(),
- new RootBeanDefinition(WebTestClientFactory.class));
+ RootBeanDefinition definition = new RootBeanDefinition(
+ WebTestClientRegistrar.class);
+ definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ registry.registerBeanDefinition(WebTestClientRegistrar.class.getName(),
+ definition);
}
@Override
@@ -77,6 +88,45 @@ class WebTestClientContextCustomizer implements ContextCustomizer {
return (obj != null && obj.getClass() == getClass());
}
+ /**
+ * {@link BeanDefinitionRegistryPostProcessor} that runs after the
+ * {@link ConfigurationClassPostProcessor} and add a {@link WebTestClientFactory} bean
+ * definition when a {@link WebTestClient} hasn't already been registered.
+ */
+ private static class WebTestClientRegistrar
+ implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
+
+ private BeanFactory beanFactory;
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
+ throws BeansException {
+ if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
+ (ListableBeanFactory) this.beanFactory,
+ WebTestClient.class).length == 0) {
+ registry.registerBeanDefinition(WebTestClient.class.getName(),
+ new RootBeanDefinition(WebTestClientFactory.class));
+ }
+
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
+ throws BeansException {
+ }
+
+ }
+
/**
* {@link FactoryBean} used to create and configure a {@link WebTestClient}.
*/
diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizerFactory.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java
similarity index 89%
rename from spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizerFactory.java
rename to spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java
index 6558ce2a71..c9e4a22f18 100644
--- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/WebTestClientContextCustomizerFactory.java
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2017 the original author or authors.
+ * Copyright 2012-2018 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.boot.test.web.reactive;
+package org.springframework.boot.test.web.reactive.server;
import java.util.List;
@@ -30,7 +30,7 @@ import org.springframework.util.ClassUtils;
*
* @author Stephane Nicoll
*/
-public class WebTestClientContextCustomizerFactory implements ContextCustomizerFactory {
+class WebTestClientContextCustomizerFactory implements ContextCustomizerFactory {
private static final String WEB_TEST_CLIENT_CLASS = "org.springframework.web.reactive.function.client.WebClient";
diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/package-info.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/package-info.java
new file mode 100644
index 0000000000..2581f9a4c3
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/web/reactive/server/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2012-2018 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.
+ */
+
+/**
+ * Spring Boot support for testing Spring WebFlux server endpoints via
+ * {@link org.springframework.test.web.reactive.server.WebTestClient}.
+ */
+package org.springframework.boot.test.web.reactive.server;
diff --git a/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories
index 1007e0aa49..aec403293e 100644
--- a/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories
@@ -5,7 +5,7 @@ org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFacto
org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\
org.springframework.boot.test.mock.mockito.MockitoContextCustomizerFactory,\
org.springframework.boot.test.web.client.TestRestTemplateTestContextCustomizerFactory,\
-org.springframework.boot.test.web.reactive.WebTestClientContextCustomizerFactory
+org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizerFactory
# Test Execution Listeners
org.springframework.test.context.TestExecutionListener=\
diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/NoWebTestClientBeanChecker.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/NoWebTestClientBeanChecker.java
new file mode 100644
index 0000000000..b25f1ba71c
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/NoWebTestClientBeanChecker.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2018 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.web.reactive.server;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.annotation.ImportSelector;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * {@link ImportSelector} to check no {@link WebTestClient} definition is registered when
+ * config classes are processed.
+ */
+class NoWebTestClientBeanChecker implements ImportSelector, BeanFactoryAware {
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
+ (ListableBeanFactory) beanFactory, WebTestClient.class)).isEmpty();
+ }
+
+ @Override
+ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
+ return new String[0];
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerIntegrationTests.java
new file mode 100644
index 0000000000..708c80aacd
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerIntegrationTests.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012-2018 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.web.reactive.server;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import reactor.core.publisher.Mono;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+/**
+ * Integration test for {@link WebTestClientContextCustomizer}.
+ *
+ * @author Phillip Webb
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
+@DirtiesContext
+public class WebTestClientContextCustomizerIntegrationTests {
+
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Test
+ public void test() {
+ this.webTestClient.get().uri("/").exchange().expectBody(String.class)
+ .isEqualTo("hello");
+ }
+
+ @Configuration
+ @Import({ TestHandler.class, NoWebTestClientBeanChecker.class })
+ static class TestConfig {
+
+ @Bean
+ public TomcatReactiveWebServerFactory webServerFactory() {
+ return new TomcatReactiveWebServerFactory(0);
+ }
+
+ }
+
+ static class TestHandler implements HttpHandler {
+
+ private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
+
+ @Override
+ public Mono handle(ServerHttpRequest request, ServerHttpResponse response) {
+ response.setStatusCode(HttpStatus.OK);
+ return response.writeWith(Mono.just(factory.wrap("hello".getBytes())));
+ }
+
+ }
+
+}
diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerWithOverrideIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerWithOverrideIntegrationTests.java
new file mode 100644
index 0000000000..943c616a16
--- /dev/null
+++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/web/reactive/server/WebTestClientContextCustomizerWithOverrideIntegrationTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2012-2018 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.web.reactive.server;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import reactor.core.publisher.Mono;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.HttpHandler;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Integration test for {@link WebTestClientContextCustomizer} with a custom
+ * {@link WebTestClient} bean.
+ *
+ * @author Phillip Webb
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
+@DirtiesContext
+public class WebTestClientContextCustomizerWithOverrideIntegrationTests {
+
+ @Autowired
+ private WebTestClient webTestClient;
+
+ @Test
+ public void test() {
+ assertThat(this.webTestClient).isInstanceOf(CustomWebTestClient.class);
+ }
+
+ @Configuration
+ @Import({ TestHandler.class, NoWebTestClientBeanChecker.class })
+ static class TestConfig {
+
+ @Bean
+ public TomcatReactiveWebServerFactory webServerFactory() {
+ return new TomcatReactiveWebServerFactory(0);
+ }
+
+ @Bean
+ public WebTestClient webTestClient() {
+ return mock(CustomWebTestClient.class);
+ }
+
+ }
+
+ static class TestHandler implements HttpHandler {
+
+ private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
+
+ @Override
+ public Mono handle(ServerHttpRequest request, ServerHttpResponse response) {
+ response.setStatusCode(HttpStatus.OK);
+ return response.writeWith(Mono.just(factory.wrap("hello".getBytes())));
+ }
+
+ }
+
+ interface CustomWebTestClient extends WebTestClient {
+
+ }
+
+}