Support embedded web environment with @AutoConfigureWebTestClient

Simplify `WebTestClient` testing support by removing the direct
context customizer in favor of using `@AutoConfigureWebTestClient`.

See gh-46356
This commit is contained in:
Andy Wilkinson 2025-10-07 15:28:08 +01:00 committed by Phillip Webb
parent 3bf53f8bf5
commit 16d8878cd8
41 changed files with 57 additions and 1577 deletions

View File

@ -1,182 +0,0 @@
/*
* 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.test.web.reactive.client;
import java.util.ArrayList;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.AotDetector;
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.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.test.http.server.BaseUrl;
import org.springframework.boot.test.http.server.BaseUrlProviders;
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.io.support.SpringFactoriesLoader;
import org.springframework.core.io.support.SpringFactoriesLoader.ArgumentResolver;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.Assert;
/**
* {@link ContextCustomizer} for {@link WebTestClient}.
*
* @author Stephane Nicoll
*/
class WebTestClientContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
if (AotDetector.useGeneratedArtifacts()) {
return;
}
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(mergedConfig.getTestClass(),
SpringBootTest.class);
Assert.state(springBootTest != null, "'springBootTest' must not be null");
if (springBootTest.webEnvironment().isEmbedded()) {
registerWebTestClient(context);
}
}
private void registerWebTestClient(ConfigurableApplicationContext context) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry registry) {
registerWebTestClient(registry);
}
}
private void registerWebTestClient(BeanDefinitionRegistry registry) {
RootBeanDefinition definition = new RootBeanDefinition(WebTestClientRegistrar.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(WebTestClientRegistrar.class.getName(), definition);
}
@Override
public boolean equals(@Nullable Object obj) {
return (obj != null) && (obj.getClass() == getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
/**
* {@link BeanDefinitionRegistryPostProcessor} that runs after the
* {@link ConfigurationClassPostProcessor} and add a {@link WebTestClientFactory} bean
* definition when a {@link WebTestClient} hasn't already been registered.
*/
static class WebTestClientRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
private @Nullable 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 (this.beanFactory == null || AotDetector.useGeneratedArtifacts()) {
return;
}
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) this.beanFactory,
WebTestClient.class, false, false).length == 0) {
registry.registerBeanDefinition(WebTestClient.class.getName(),
new RootBeanDefinition(WebTestClientFactory.class));
}
}
}
/**
* {@link FactoryBean} used to create and configure a {@link WebTestClient}.
*/
public static class WebTestClientFactory implements FactoryBean<WebTestClient>, ApplicationContextAware {
private @Nullable ApplicationContext applicationContext;
private @Nullable WebTestClient object;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public Class<?> getObjectType() {
return WebTestClient.class;
}
@Override
public WebTestClient getObject() throws Exception {
if (this.object == null) {
this.object = createWebTestClient();
}
return this.object;
}
private WebTestClient createWebTestClient() {
Assert.state(this.applicationContext != null, "ApplicationContext not injected");
WebTestClient.Builder builder = WebTestClient.bindToServer();
customizeWebTestClientBuilder(builder);
BaseUrl baseUrl = new BaseUrlProviders(this.applicationContext).getBaseUrl();
if (baseUrl != null) {
builder.baseUrl(baseUrl.resolve());
}
return builder.build();
}
private void customizeWebTestClientBuilder(WebTestClient.Builder clientBuilder) {
Assert.state(this.applicationContext != null, "ApplicationContext not injected");
getWebTestClientBuilderCustomizers(this.applicationContext)
.forEach((customizer) -> customizer.customize(clientBuilder));
}
private List<WebTestClientBuilderCustomizer> getWebTestClientBuilderCustomizers(ApplicationContext context) {
List<WebTestClientBuilderCustomizer> customizers = new ArrayList<>();
customizers.addAll(SpringFactoriesLoader.forDefaultResourceLocation(context.getClassLoader())
.load(WebTestClientBuilderCustomizer.class, ArgumentResolver.of(ApplicationContext.class, context)));
customizers.addAll(context.getBeansOfType(WebTestClientBuilderCustomizer.class).values());
return customizers;
}
}
}

View File

@ -1,54 +0,0 @@
/*
* 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.test.web.reactive.client;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.util.ClassUtils;
/**
* {@link ContextCustomizerFactory} for {@code WebTestClient}.
*
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Anugrah Singhal
*/
class WebTestClientContextCustomizerFactory implements ContextCustomizerFactory {
private static final boolean webClientPresent;
static {
ClassLoader loader = WebTestClientContextCustomizerFactory.class.getClassLoader();
webClientPresent = ClassUtils.isPresent("org.springframework.web.reactive.function.client.WebClient", loader);
}
@Override
public @Nullable ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
SpringBootTest.class);
return (springBootTest != null && webClientPresent) ? new WebTestClientContextCustomizer() : null;
}
}

View File

@ -6,9 +6,8 @@ org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizerFacto
org.springframework.boot.test.context.filter.annotation.TypeExcludeFiltersContextCustomizerFactory,\
org.springframework.boot.test.http.client.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory,\
org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory,\
org.springframework.boot.test.web.reactive.client.WebTestClientContextCustomizerFactory,\
org.springframework.boot.test.web.servlet.client.RestTestClientContextCustomizerFactory
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.test.context.filter.ExcludeFilterApplicationContextInitializer
org.springframework.boot.test.context.filter.ExcludeFilterApplicationContextInitializer

View File

@ -1,92 +0,0 @@
/*
* 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.test.web.reactive.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.web.reactive.client.WebTestClientContextCustomizer.WebTestClientRegistrar;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link WebTestClientContextCustomizer}.
*
* @author Moritz Halbritter
*/
class WebTestClientContextCustomizerTests {
@Test
void whenContextIsNotABeanDefinitionRegistryWebTestClientIsRegistered() {
new ApplicationContextRunner(TestApplicationContext::new)
.withInitializer(this::applyWebTestClientContextCustomizer)
.run((context) -> assertThat(context).hasSingleBean(WebTestClient.class));
}
@Test
void whenUsingAotGeneratedArtifactsWebTestClientIsNotRegistered() {
new ApplicationContextRunner().withSystemProperties("spring.aot.enabled:true")
.withInitializer(this::applyWebTestClientContextCustomizer)
.run((context) -> {
assertThat(context).doesNotHaveBean(WebTestClientRegistrar.class);
assertThat(context).doesNotHaveBean(WebTestClient.class);
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
void applyWebTestClientContextCustomizer(ConfigurableApplicationContext context) {
MergedContextConfiguration configuration = mock(MergedContextConfiguration.class);
given(configuration.getTestClass()).willReturn((Class) TestClass.class);
new WebTestClientContextCustomizer().customizeContext(context, configuration);
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
static class TestClass {
}
static class TestApplicationContext extends AbstractApplicationContext {
private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@Override
protected void refreshBeanFactory() {
}
@Override
protected void closeBeanFactory() {
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
}
}

View File

@ -28,6 +28,7 @@ dependencies {
testImplementation(project(":module:spring-boot-http-codec"))
testImplementation(project(":module:spring-boot-restclient-test"))
testImplementation(project(":module:spring-boot-tomcat"))
testImplementation(project(":module:spring-boot-webflux-test"))
testImplementation(project(":module:spring-boot-web-server"))
testImplementation("io.projectreactor.netty:reactor-netty-http")
testImplementation("org.springframework:spring-webmvc")

View File

@ -1,47 +0,0 @@
/*
* 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.test.web.reactive.client;
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) {
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) beanFactory,
WebTestClient.class))
.isEmpty();
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[0];
}
}

View File

@ -1,91 +0,0 @@
/*
* 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.test.web.reactive.client;
import org.junit.jupiter.api.Test;
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.tomcat.reactive.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.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient.Builder;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
* Integration test for {@link WebTestClientContextCustomizer}.
*
* @author Phillip Webb
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
@DirtiesContext
class WebTestClientContextCustomizerIntegrationTests {
@Autowired
private WebTestClient webTestClient;
@Autowired
private WebTestClientBuilderCustomizer clientBuilderCustomizer;
@Test
void test() {
then(this.clientBuilderCustomizer).should().customize(any(Builder.class));
this.webTestClient.get().uri("/").exchange().expectBody(String.class).isEqualTo("hello");
}
@Configuration(proxyBeanMethods = false)
@Import({ TestHandler.class, NoWebTestClientBeanChecker.class })
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
WebTestClientBuilderCustomizer clientBuilderCustomizer() {
return mock(WebTestClientBuilderCustomizer.class);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
return response.writeWith(Mono.just(factory.wrap("hello".getBytes())));
}
}
}

View File

@ -1,125 +0,0 @@
/*
* 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.test.web.reactive.client;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.reactive.TomcatReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
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.context.TestPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Tests for {@link WebTestClientContextCustomizer} when the test context framework pauses
* a context while it's in the cache.
*
* @author Andy Wilkinson
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.main.web-application-type=none")
class WebTestClientContextCustomizerTcfCacheContextPausingTests {
@Nested
@Import(TestConfig.class)
@TestPropertySource(properties = { "context=one", "spring.main.web-application-type=reactive" })
class ContextOne {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
this.webTestClient.get().uri("/test").exchange().expectBody(String.class).isEqualTo("hello world");
}
}
@Nested
@Import(TestConfig.class)
@TestPropertySource(properties = { "context=two", "spring.main.web-application-type=reactive" })
class ContextTwo {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
this.webTestClient.get().uri("/test").exchange().expectBody(String.class).isEqualTo("hello world");
}
}
@Nested
@Import(TestConfig.class)
@TestPropertySource(properties = { "context=one", "spring.main.web-application-type=reactive" })
class ReuseContextOne {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
this.webTestClient.get().uri("/test").exchange().expectBody(String.class).isEqualTo("hello world");
}
}
@SpringBootConfiguration(proxyBeanMethods = false)
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
HttpHandler httpHandler() {
TestHandler httpHandler = new TestHandler();
Map<String, HttpHandler> handlersMap = Collections.singletonMap("/test", httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
return response.writeWith(Mono.just(factory.wrap("hello world".getBytes())));
}
}
}

View File

@ -1,87 +0,0 @@
/*
* 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.test.web.reactive.client;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.reactive.TomcatReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
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.context.TestPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Tests for {@link WebTestClientContextCustomizer} with a custom base path for a reactive
* web application.
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.main.web-application-type=reactive")
@TestPropertySource(properties = "spring.webflux.base-path=/test")
class WebTestClientContextCustomizerWithCustomBasePathTests {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
this.webTestClient.get().uri("/hello").exchange().expectBody(String.class).isEqualTo("hello world");
}
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
HttpHandler httpHandler() {
TestHandler httpHandler = new TestHandler();
Map<String, HttpHandler> handlersMap = Collections.singletonMap("/test", httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
return response.writeWith(Mono.just(factory.wrap("hello world".getBytes())));
}
}
}

View File

@ -1,79 +0,0 @@
/*
* 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.test.web.reactive.client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Tests for {@link WebTestClientContextCustomizer} with a custom context path for a
* servlet web application.
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = "server.servlet.context-path=/test")
class WebTestClientContextCustomizerWithCustomContextPathTests {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
this.webTestClient.get().uri("/hello").exchange().expectBody(String.class).isEqualTo("hello world");
}
@Configuration(proxyBeanMethods = false)
@Import(TestController.class)
static class TestConfig {
@Bean
TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0);
factory.setContextPath("/test");
return factory;
}
@Bean
DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}
@RestController
static class TestController {
@GetMapping("/hello")
String hello() {
return "hello world";
}
}
}

View File

@ -1,90 +0,0 @@
/*
* 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.test.web.reactive.client;
import org.junit.jupiter.api.Test;
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.tomcat.reactive.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.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
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
@DirtiesContext
class WebTestClientContextCustomizerWithOverrideIntegrationTests {
@Autowired
private WebTestClient webTestClient;
@Test
void test() {
assertThat(this.webTestClient).isInstanceOf(CustomWebTestClient.class);
}
@Configuration(proxyBeanMethods = false)
@Import({ TestHandler.class, NoWebTestClientBeanChecker.class })
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
WebTestClient webTestClient() {
return mock(CustomWebTestClient.class);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
return response.writeWith(Mono.just(factory.wrap("hello".getBytes())));
}
}
interface CustomWebTestClient extends WebTestClient {
}
}

View File

@ -1,52 +0,0 @@
/*
* 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.test.web.reactive.client;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.test.context.ContextCustomizer;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link WebTestClientContextCustomizerFactory} when spring webflux is not on
* the classpath.
*
* @author Tobias Gesellchen
* @author Stephane Nicoll
*/
@ClassPathExclusions("spring-webflux*.jar")
class WebTestClientContextCustomizerWithoutWebfluxIntegrationTests {
@Test
void customizerIsNotCreatedWithoutWebClient() {
WebTestClientContextCustomizerFactory contextCustomizerFactory = new WebTestClientContextCustomizerFactory();
ContextCustomizer contextCustomizer = contextCustomizerFactory.createContextCustomizer(TestClass.class,
Collections.emptyList());
assertThat(contextCustomizer).isNull();
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
private static final class TestClass {
}
}

View File

@ -30,6 +30,7 @@ import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.tomcat.reactive.TomcatReactiveWebServerFactory;
import org.springframework.boot.web.context.reactive.ReactiveWebApplicationContext;
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -47,6 +48,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Stephane Nicoll
*/
@AutoConfigureWebTestClient
@AutoConfigureTestRestTemplate
abstract class AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {

View File

@ -31,11 +31,6 @@ import org.springframework.graphql.test.tester.HttpGraphQlTester;
/**
* Annotation that can be applied to a test class to enable an {@link HttpGraphQlTester}.
*
* <p>
* This annotation should be used with
* {@link org.springframework.boot.test.context.SpringBootTest @SpringBootTest} tests with
* Spring MVC or Spring WebFlux mock infrastructures.
*
* @author Brian Clozel
* @since 4.0.0
* @see HttpGraphQlTesterAutoConfiguration

View File

@ -1,232 +0,0 @@
/*
* 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.graphql.test.tester;
import jakarta.servlet.ServletContext;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.AotDetector;
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.WebApplicationType;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
/**
* {@link ContextCustomizer} for {@link HttpGraphQlTester}.
*
* @author Brian Clozel
*/
class HttpGraphQlTesterContextCustomizer implements ContextCustomizer {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
if (AotDetector.useGeneratedArtifacts()) {
return;
}
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(mergedConfig.getTestClass(),
SpringBootTest.class);
Assert.state(springBootTest != null, "'springBootTest' must not be null");
if (springBootTest.webEnvironment().isEmbedded()) {
registerHttpGraphQlTester(context);
}
}
private void registerHttpGraphQlTester(ConfigurableApplicationContext context) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory instanceof BeanDefinitionRegistry beanDefinitionRegistry) {
registerHttpGraphQlTester(beanDefinitionRegistry);
}
}
private void registerHttpGraphQlTester(BeanDefinitionRegistry registry) {
RootBeanDefinition definition = new RootBeanDefinition(HttpGraphQlTesterRegistrar.class);
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(HttpGraphQlTesterRegistrar.class.getName(), definition);
}
@Override
public boolean equals(@Nullable Object obj) {
return (obj != null) && (obj.getClass() == getClass());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
static class HttpGraphQlTesterRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
@SuppressWarnings("NullAway.Init")
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
if (AotDetector.useGeneratedArtifacts()) {
return;
}
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) this.beanFactory,
HttpGraphQlTester.class, false, false).length == 0) {
registry.registerBeanDefinition(HttpGraphQlTester.class.getName(),
new RootBeanDefinition(HttpGraphQlTesterFactory.class));
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 1;
}
}
public static class HttpGraphQlTesterFactory implements FactoryBean<HttpGraphQlTester>, ApplicationContextAware {
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.context.reactive.ReactiveWebApplicationContext";
@SuppressWarnings("NullAway.Init")
private ApplicationContext applicationContext;
private @Nullable HttpGraphQlTester object;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public Class<?> getObjectType() {
return HttpGraphQlTester.class;
}
@Override
public HttpGraphQlTester getObject() throws Exception {
if (this.object == null) {
this.object = createGraphQlTester();
}
return this.object;
}
private HttpGraphQlTester createGraphQlTester() {
WebTestClient webTestClient = this.applicationContext.getBean(WebTestClient.class);
boolean sslEnabled = isSslEnabled(this.applicationContext);
String port = this.applicationContext.getEnvironment().getProperty("local.server.port", "8080");
WebTestClient mutatedWebClient = webTestClient.mutate().baseUrl(getBaseUrl(sslEnabled, port)).build();
return HttpGraphQlTester.create(mutatedWebClient);
}
private String getBaseUrl(boolean sslEnabled, String port) {
String basePath = deduceBasePath();
return (sslEnabled ? "https" : "http") + "://localhost:" + port + basePath;
}
private String deduceBasePath() {
return deduceServerBasePath() + findConfiguredGraphQlPath();
}
private String findConfiguredGraphQlPath() {
String configuredPath = this.applicationContext.getEnvironment().getProperty("spring.graphql.http.path");
return StringUtils.hasText(configuredPath) ? configuredPath : "/graphql";
}
private String deduceServerBasePath() {
String serverBasePath = "";
WebApplicationType webApplicationType = deduceFromApplicationContext(this.applicationContext.getClass());
if (webApplicationType == WebApplicationType.REACTIVE) {
serverBasePath = this.applicationContext.getEnvironment().getProperty("spring.webflux.base-path");
}
else if (webApplicationType == WebApplicationType.SERVLET) {
ServletContext servletContext = ((WebApplicationContext) this.applicationContext).getServletContext();
Assert.state(servletContext != null, "'servletContext' must not be null");
serverBasePath = servletContext.getContextPath();
}
return (serverBasePath != null) ? serverBasePath : "";
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
private boolean isSslEnabled(ApplicationContext context) {
try {
AbstractConfigurableWebServerFactory webServerFactory = context
.getBean(AbstractConfigurableWebServerFactory.class);
return webServerFactory.getSsl() != null && webServerFactory.getSsl().isEnabled();
}
catch (NoSuchBeanDefinitionException ex) {
return false;
}
}
}
}

View File

@ -1,56 +0,0 @@
/*
* 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.graphql.test.tester;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.TestContextAnnotationUtils;
import org.springframework.util.ClassUtils;
/**
* {@link ContextCustomizerFactory} for {@link HttpGraphQlTester}.
*
* @author Brian Clozel
* @see HttpGraphQlTesterContextCustomizer
*/
class HttpGraphQlTesterContextCustomizerFactory implements ContextCustomizerFactory {
private static final String HTTPGRAPHQLTESTER_CLASS = "org.springframework.graphql.test.tester.HttpGraphQlTester";
private static final String WEBTESTCLIENT_CLASS = "org.springframework.test.web.reactive.server.WebTestClient";
@Override
public @Nullable ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
SpringBootTest.class);
return (springBootTest != null && isGraphQlTesterPresent()) ? new HttpGraphQlTesterContextCustomizer() : null;
}
private boolean isGraphQlTesterPresent() {
return ClassUtils.isPresent(WEBTESTCLIENT_CLASS, getClass().getClassLoader())
&& ClassUtils.isPresent(HTTPGRAPHQLTESTER_CLASS, getClass().getClassLoader());
}
}

View File

@ -1,23 +0,0 @@
/*
* 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.
*/
/**
* {@link org.springframework.graphql.test.tester.GraphQlTester} utilities.
*/
@NullMarked
package org.springframework.boot.graphql.test.tester;
import org.jspecify.annotations.NullMarked;

View File

@ -1,3 +0,0 @@
# Spring Test Context Customizer Factories
org.springframework.test.context.ContextCustomizerFactory=\
org.springframework.boot.graphql.test.tester.HttpGraphQlTesterContextCustomizerFactory

View File

@ -1,88 +0,0 @@
/*
* 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.graphql.test.tester;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.reactive.TomcatReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
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;
/**
* Integration test for {@link HttpGraphQlTesterContextCustomizer}.
*
* @author Brian Clozel
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "spring.main.web-application-type=reactive")
@DirtiesContext
class HttpGraphQlTesterContextCustomizerIntegrationTests {
@Autowired
HttpGraphQlTester graphQlTester;
@Test
void shouldHandleGraphQlRequests() {
this.graphQlTester.document("{}").executeAndVerify();
}
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
HttpHandler httpHandler() {
TestHandler httpHandler = new TestHandler();
Map<String, HttpHandler> handlersMap = Collections.singletonMap("/graphql", httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
}
}
}

View File

@ -1,92 +0,0 @@
/*
* 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.graphql.test.tester;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.graphql.test.tester.HttpGraphQlTesterContextCustomizer.HttpGraphQlTesterRegistrar;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.test.context.MergedContextConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for HttpGraphQlTesterContextCustomizer.
*
* @author Moritz Halbritter
*/
class HttpGraphQlTesterContextCustomizerTests {
@Test
void whenContextIsNotABeanDefinitionRegistryHttpGraphQlTesterIsRegistered() {
new ApplicationContextRunner(HttpGraphQlTesterContextCustomizerTests.TestApplicationContext::new)
.withInitializer(this::applyHttpGraphQlTesterContextCustomizer)
.run((context) -> assertThat(context).hasSingleBean(HttpGraphQlTester.class));
}
@Test
void whenUsingAotGeneratedArtifactsHttpGraphQlTesterIsNotRegistered() {
new ApplicationContextRunner().withSystemProperties("spring.aot.enabled:true")
.withInitializer(this::applyHttpGraphQlTesterContextCustomizer)
.run((context) -> {
assertThat(context).doesNotHaveBean(HttpGraphQlTesterRegistrar.class);
assertThat(context).doesNotHaveBean(HttpGraphQlTester.class);
});
}
@SuppressWarnings({ "unchecked", "rawtypes" })
void applyHttpGraphQlTesterContextCustomizer(ConfigurableApplicationContext context) {
MergedContextConfiguration configuration = mock(MergedContextConfiguration.class);
given(configuration.getTestClass()).willReturn((Class) HttpGraphQlTesterContextCustomizerTests.TestClass.class);
new HttpGraphQlTesterContextCustomizer().customizeContext(context, configuration);
}
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
static class TestClass {
}
static class TestApplicationContext extends AbstractApplicationContext {
private final ConfigurableListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@Override
protected void refreshBeanFactory() {
}
@Override
protected void closeBeanFactory() {
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
}
}

View File

@ -1,88 +0,0 @@
/*
* 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.graphql.test.tester;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.reactive.TomcatReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ContextPathCompositeHandler;
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.context.TestPropertySource;
/**
* Tests for {@link HttpGraphQlTesterContextCustomizer} with a custom context path for a
* Reactive web application.
*
* @author Brian Clozel
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = { "spring.main.web-application-type=reactive", "spring.webflux.base-path=/test" })
class HttpGraphQlTesterContextCustomizerWithCustomBasePathTests {
@Autowired
HttpGraphQlTester graphQlTester;
@Test
void shouldHandleGraphQlRequests() {
this.graphQlTester.document("{}").executeAndVerify();
}
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean
TomcatReactiveWebServerFactory webServerFactory() {
return new TomcatReactiveWebServerFactory(0);
}
@Bean
HttpHandler httpHandler() {
TestHandler httpHandler = new TestHandler();
Map<String, HttpHandler> handlersMap = Collections.singletonMap("/test/graphql", httpHandler);
return new ContextPathCompositeHandler(handlersMap);
}
}
static class TestHandler implements HttpHandler {
private static final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
response.setStatusCode(HttpStatus.OK);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
return response.writeWith(Mono.just(factory.wrap("{\"data\":{}}".getBytes())));
}
}
}

View File

@ -1,80 +0,0 @@
/*
* 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.graphql.test.tester;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Tests for {@link HttpGraphQlTesterContextCustomizer} with a custom context path for a
* Servlet web application.
*
* @author Brian Clozel
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = "server.servlet.context-path=/test")
class HttpGraphQlTesterContextCustomizerWithCustomContextPathTests {
@Autowired
HttpGraphQlTester graphQlTester;
@Test
void shouldHandleGraphQlRequests() {
this.graphQlTester.document("{}").executeAndVerify();
}
@Configuration(proxyBeanMethods = false)
@Import(TestController.class)
static class TestConfig {
@Bean
TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0);
factory.setContextPath("/test");
return factory;
}
@Bean
DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}
@RestController
static class TestController {
@PostMapping(path = "/graphql", produces = MediaType.APPLICATION_JSON_VALUE)
String graphql() {
return "{}";
}
}
}

View File

@ -31,6 +31,7 @@ import org.springframework.boot.mustache.reactive.view.MustacheViewResolver;
import org.springframework.boot.reactor.netty.autoconfigure.NettyReactiveWebServerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -56,6 +57,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Moritz Halbritter
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.main.web-application-type=reactive")
@AutoConfigureWebTestClient
class MustacheAutoConfigurationReactiveIntegrationTests {
@Autowired

View File

@ -26,7 +26,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.http.codec.CodecCustomizer;
import org.springframework.boot.test.http.server.BaseUrl;
import org.springframework.boot.test.http.server.BaseUrlProviders;
import org.springframework.boot.test.web.reactive.client.WebTestClientBuilderCustomizer;
import org.springframework.boot.test.web.servlet.client.BaseUrlUriBuilderFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.test.web.reactive.server.MockServerConfigurer;
@ -52,17 +55,26 @@ public final class WebTestClientAutoConfiguration {
@ConditionalOnBean(WebHandler.class)
WebTestClient webTestClient(ApplicationContext applicationContext, List<WebTestClientBuilderCustomizer> customizers,
List<MockServerConfigurer> configurers) {
WebTestClient.MockServerSpec<?> mockServerSpec = WebTestClient.bindToApplicationContext(applicationContext);
for (MockServerConfigurer configurer : configurers) {
mockServerSpec.apply(configurer);
}
WebTestClient.Builder builder = mockServerSpec.configureClient();
WebTestClient.Builder builder = prepareBuilder(applicationContext, configurers);
for (WebTestClientBuilderCustomizer customizer : customizers) {
customizer.customize(builder);
}
return builder.build();
}
private WebTestClient.Builder prepareBuilder(ApplicationContext applicationContext,
List<MockServerConfigurer> configurers) {
BaseUrl baseUrl = new BaseUrlProviders(applicationContext).getBaseUrlOrDefault();
if (baseUrl == BaseUrl.DEFAULT) {
WebTestClient.MockServerSpec<?> mockServerSpec = WebTestClient.bindToApplicationContext(applicationContext);
for (MockServerConfigurer configurer : configurers) {
mockServerSpec.apply(configurer);
}
return mockServerSpec.configureClient();
}
return WebTestClient.bindToServer().uriBuilderFactory(BaseUrlUriBuilderFactory.get(baseUrl));
}
@Bean
@ConfigurationProperties("spring.test.webtestclient")
SpringBootWebTestClientBuilderCustomizer springBootWebTestClientBuilderCustomizer(

View File

@ -28,4 +28,5 @@ dependencies {
runtimeOnly("io.r2dbc:r2dbc-h2")
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
}

View File

@ -24,12 +24,14 @@ import org.junit.jupiter.api.Test;
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.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.context.ApplicationContext;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "spring.r2dbc.generate-unique-name=true")
@AutoConfigureWebTestClient
class SampleR2dbcApplicationTests {
@Autowired

View File

@ -26,5 +26,6 @@ dependencies {
implementation(project(":starter:spring-boot-starter-webflux"))
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
testImplementation("org.apache.httpcomponents.client5:httpclient5")
}

View File

@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.assertj.core.api.Assertions.assertThat;
@ -27,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = { "APP-CLIENT-ID=my-client-id", "APP-CLIENT-SECRET=my-client-secret",
"YAHOO-CLIENT-ID=my-google-client-id", "YAHOO-CLIENT-SECRET=my-google-client-secret" })
@AutoConfigureWebTestClient
class SampleReactiveOAuth2ClientApplicationTests {
@Autowired

View File

@ -25,5 +25,6 @@ dependencies {
implementation(project(":starter:spring-boot-starter-webflux"))
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
testImplementation("com.squareup.okhttp3:mockwebserver")
}

View File

@ -24,11 +24,13 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
class SampleReactiveOAuth2ResourceServerApplicationTests {
@Autowired

View File

@ -26,5 +26,6 @@ dependencies {
implementation(project(":starter:spring-boot-starter-webflux"))
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
testImplementation("io.projectreactor:reactor-test")
}

View File

@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
@ -30,6 +31,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("cors")
@AutoConfigureWebTestClient
class CorsSampleActuatorApplicationTests {
@Autowired

View File

@ -28,6 +28,7 @@ import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalManagementPort;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
@ -42,8 +43,10 @@ import static org.springframework.security.config.Customizer.withDefaults;
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0" }, classes = {
ManagementPortSampleSecureWebFluxTests.SecurityConfiguration.class, SampleSecureWebFluxApplication.class })
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "management.server.port=0" },
classes = { ManagementPortSampleSecureWebFluxTests.SecurityConfiguration.class,
SampleSecureWebFluxApplication.class })
@AutoConfigureWebTestClient
class ManagementPortSampleSecureWebFluxTests {
@LocalServerPort

View File

@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@ -33,6 +34,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "management.endpoint.health.show-details=never")
@AutoConfigureWebTestClient
class SampleSecureWebFluxApplicationTests {
@Autowired

View File

@ -25,6 +25,7 @@ import org.springframework.boot.actuate.web.mappings.MappingsEndpoint;
import org.springframework.boot.security.autoconfigure.actuate.reactive.EndpointRequest;
import org.springframework.boot.security.autoconfigure.reactive.PathRequest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@ -42,8 +43,10 @@ import static org.springframework.security.config.Customizer.withDefaults;
*
* @author Madhura Bhave
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {
SampleSecureWebFluxCustomSecurityTests.SecurityConfiguration.class, SampleSecureWebFluxApplication.class })
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = { SampleSecureWebFluxCustomSecurityTests.SecurityConfiguration.class,
SampleSecureWebFluxApplication.class })
@AutoConfigureWebTestClient
class SampleSecureWebFluxCustomSecurityTests {
@Autowired

View File

@ -29,6 +29,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
testImplementation("io.projectreactor:reactor-test")
}

View File

@ -23,8 +23,10 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment
import org.springframework.http.MediaType
import org.springframework.test.web.reactive.server.WebTestClient
import org.springframework.test.web.reactive.server.expectBody
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
class CoroutinesControllerTests(@Autowired private val webClient: WebTestClient) {
@Test

View File

@ -27,5 +27,6 @@ dependencies {
testImplementation(project(":starter:spring-boot-starter-restclient-test"))
testImplementation(project(":starter:spring-boot-starter-test"))
testImplementation(project(":starter:spring-boot-starter-webflux-test"))
testImplementation("io.projectreactor:reactor-test")
}

View File

@ -21,6 +21,7 @@ import tools.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;
@ -34,6 +35,7 @@ import org.springframework.test.web.reactive.server.WebTestClient;
properties = { "management.endpoints.jackson.isolated-object-mapper=false",
"spring.jackson.mapper.require-setters-for-getters=true" })
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
@AutoConfigureWebTestClient
class SampleWebFluxApplicationActuatorIsolatedObjectMapperFalseTests {
@Autowired

View File

@ -23,6 +23,7 @@ import tools.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
@ -39,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThat;
properties = { "management.endpoints.jackson.isolated-object-mapper=true",
"spring.jackson.mapper.require-setters-for-getters=true" })
@ContextConfiguration(loader = ApplicationStartupSpringBootContextLoader.class)
@AutoConfigureWebTestClient
class SampleWebFluxApplicationActuatorIsolatedObjectMapperTrueTests {
@Autowired

View File

@ -22,6 +22,7 @@ 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.webflux.test.autoconfigure.AutoConfigureWebTestClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
@ -34,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Brian Clozel
*/
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "server.error.include-message=always")
@AutoConfigureWebTestClient
class SampleWebFluxApplicationTests {
@Autowired