Create spring-boot-web-server-test module
This commit is contained in:
committed by
Phillip Webb
parent
96bee8e034
commit
4f579905ef
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* Annotation at the field or method/constructor parameter level that injects the HTTP
|
||||
* management port that was allocated at runtime. Provides a convenient alternative for
|
||||
* <code>@Value("${local.management.port}")</code>.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Value("${local.management.port}")
|
||||
public @interface LocalManagementPort {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
/**
|
||||
* Annotation at the field or method/constructor parameter level that injects the HTTP
|
||||
* server port that was allocated at runtime. Provides a convenient alternative for
|
||||
* <code>@Value("${local.server.port}")</code>.
|
||||
*
|
||||
* @author Anand Shah
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Value("${local.server.port}")
|
||||
public @interface LocalServerPort {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link EnvironmentPostProcessor} implementation to start the management context on a
|
||||
* random port if the main server's port is 0 and the management context is expected on a
|
||||
* different port.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootTestRandomPortEnvironmentPostProcessor implements EnvironmentPostProcessor {
|
||||
|
||||
private static final String MANAGEMENT_PORT_PROPERTY = "management.server.port";
|
||||
|
||||
private static final String SERVER_PORT_PROPERTY = "server.port";
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
MapPropertySource source = (MapPropertySource) environment.getPropertySources()
|
||||
.get(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
if (source == null || isTestServerPortFixed(source, environment) || isTestManagementPortConfigured(source)) {
|
||||
return;
|
||||
}
|
||||
Integer managementPort = getPropertyAsInteger(environment, MANAGEMENT_PORT_PROPERTY, null);
|
||||
if (managementPort == null || managementPort.equals(-1) || managementPort.equals(0)) {
|
||||
return;
|
||||
}
|
||||
Integer serverPort = getPropertyAsInteger(environment, SERVER_PORT_PROPERTY, 8080);
|
||||
if (!managementPort.equals(serverPort)) {
|
||||
source.getSource().put(MANAGEMENT_PORT_PROPERTY, "0");
|
||||
}
|
||||
else {
|
||||
source.getSource().put(MANAGEMENT_PORT_PROPERTY, "");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTestServerPortFixed(MapPropertySource source, ConfigurableEnvironment environment) {
|
||||
return !Integer.valueOf(0).equals(getPropertyAsInteger(source, SERVER_PORT_PROPERTY, environment));
|
||||
}
|
||||
|
||||
private boolean isTestManagementPortConfigured(PropertySource<?> source) {
|
||||
return source.getProperty(MANAGEMENT_PORT_PROPERTY) != null;
|
||||
}
|
||||
|
||||
private Integer getPropertyAsInteger(ConfigurableEnvironment environment, String property, Integer defaultValue) {
|
||||
return environment.getPropertySources()
|
||||
.stream()
|
||||
.filter((source) -> !source.getName()
|
||||
.equals(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME))
|
||||
.map((source) -> getPropertyAsInteger(source, property, environment))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(defaultValue);
|
||||
}
|
||||
|
||||
private Integer getPropertyAsInteger(PropertySource<?> source, String property,
|
||||
ConfigurableEnvironment environment) {
|
||||
Object value = source.getProperty(property);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (ClassUtils.isAssignableValue(Integer.class, value)) {
|
||||
return (Integer) value;
|
||||
}
|
||||
try {
|
||||
return environment.getConversionService().convert(value, Integer.class);
|
||||
}
|
||||
catch (ConversionFailedException ex) {
|
||||
if (value instanceof String string) {
|
||||
return getResolvedValueIfPossible(environment, string);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getResolvedValueIfPossible(ConfigurableEnvironment environment, String value) {
|
||||
String resolvedValue = environment.resolveRequiredPlaceholders(value);
|
||||
return environment.getConversionService().convert(resolvedValue, Integer.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
import org.springframework.boot.web.client.RootUriTemplateHandler;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
import org.springframework.web.util.UriTemplateHandler;
|
||||
|
||||
/**
|
||||
* {@link UriTemplateHandler} will automatically prefix relative URIs with
|
||||
* <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
* @author Madhura Bhave
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class LocalHostUriTemplateHandler extends RootUriTemplateHandler {
|
||||
|
||||
private static final String PREFIX = "server.servlet.";
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final String scheme;
|
||||
|
||||
/**
|
||||
* Create a new {@code LocalHostUriTemplateHandler} that will generate {@code http}
|
||||
* URIs using the given {@code environment} to determine the context path and port.
|
||||
* @param environment the environment used to determine the port
|
||||
*/
|
||||
public LocalHostUriTemplateHandler(Environment environment) {
|
||||
this(environment, "http");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code LocalHostUriTemplateHandler} that will generate URIs with the
|
||||
* given {@code scheme} and use the given {@code environment} to determine the
|
||||
* context-path and port.
|
||||
* @param environment the environment used to determine the port
|
||||
* @param scheme the scheme of the root uri
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public LocalHostUriTemplateHandler(Environment environment, String scheme) {
|
||||
this(environment, scheme, new DefaultUriBuilderFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code LocalHostUriTemplateHandler} that will generate URIs with the
|
||||
* given {@code scheme}, use the given {@code environment} to determine the
|
||||
* context-path and port and delegate to the given template {@code handler}.
|
||||
* @param environment the environment used to determine the port
|
||||
* @param scheme the scheme of the root uri
|
||||
* @param handler the delegate handler
|
||||
* @since 2.0.3
|
||||
*/
|
||||
public LocalHostUriTemplateHandler(Environment environment, String scheme, UriTemplateHandler handler) {
|
||||
super(handler);
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
Assert.notNull(scheme, "'scheme' must not be null");
|
||||
this.environment = environment;
|
||||
this.scheme = scheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootUri() {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
String contextPath = this.environment.getProperty(PREFIX + "context-path", "");
|
||||
return this.scheme + "://localhost:" + port + contextPath;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
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.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory;
|
||||
import org.springframework.boot.web.server.test.client.TestRestTemplate.HttpClientOption;
|
||||
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.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} for {@link TestRestTemplate}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class TestRestTemplateContextCustomizer implements ContextCustomizer {
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
if (AotDetector.useGeneratedArtifacts()) {
|
||||
return;
|
||||
}
|
||||
SpringBootTest springBootTest = TestContextAnnotationUtils
|
||||
.findMergedAnnotation(mergedContextConfiguration.getTestClass(), SpringBootTest.class);
|
||||
if (springBootTest.webEnvironment().isEmbedded()) {
|
||||
registerTestRestTemplate(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTestRestTemplate(ConfigurableApplicationContext context) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beanFactory instanceof BeanDefinitionRegistry registry) {
|
||||
registerTestRestTemplate(registry);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTestRestTemplate(BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(TestRestTemplateRegistrar.class);
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(TestRestTemplateRegistrar.class.getName(), definition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(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 TestRestTemplateFactory}
|
||||
* bean definition when a {@link TestRestTemplate} hasn't already been registered.
|
||||
*/
|
||||
static class TestRestTemplateRegistrar 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 (AotDetector.useGeneratedArtifacts()) {
|
||||
return;
|
||||
}
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) this.beanFactory,
|
||||
TestRestTemplate.class, false, false).length == 0) {
|
||||
registry.registerBeanDefinition(TestRestTemplate.class.getName(),
|
||||
new RootBeanDefinition(TestRestTemplateFactory.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} used to create and configure a {@link TestRestTemplate}.
|
||||
*/
|
||||
public static class TestRestTemplateFactory implements FactoryBean<TestRestTemplate>, ApplicationContextAware {
|
||||
|
||||
private static final HttpClientOption[] DEFAULT_OPTIONS = {};
|
||||
|
||||
private static final HttpClientOption[] SSL_OPTIONS = { HttpClientOption.SSL };
|
||||
|
||||
private TestRestTemplate template;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
RestTemplateBuilder builder = getRestTemplateBuilder(applicationContext);
|
||||
boolean sslEnabled = isSslEnabled(applicationContext);
|
||||
TestRestTemplate template = new TestRestTemplate(builder, null, null,
|
||||
sslEnabled ? SSL_OPTIONS : DEFAULT_OPTIONS);
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(applicationContext.getEnvironment(),
|
||||
sslEnabled ? "https" : "http");
|
||||
template.setUriTemplateHandler(handler);
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
private boolean isSslEnabled(ApplicationContext context) {
|
||||
try {
|
||||
AbstractConfigurableWebServerFactory webServerFactory = context
|
||||
.getBean(AbstractConfigurableWebServerFactory.class);
|
||||
return webServerFactory.getSsl() != null && webServerFactory.getSsl().isEnabled();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private RestTemplateBuilder getRestTemplateBuilder(ApplicationContext applicationContext) {
|
||||
try {
|
||||
return applicationContext.getBean(RestTemplateBuilder.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return new RestTemplateBuilder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return TestRestTemplate.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestRestTemplate getObject() throws Exception {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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.web.server.test.client;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} for {@link TestRestTemplate}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @see TestRestTemplateContextCustomizer
|
||||
*/
|
||||
class TestRestTemplateContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
|
||||
SpringBootTest.class);
|
||||
return (springBootTest != null) ? new TestRestTemplateContextCustomizer() : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Client-side support for testing embedded web servers.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test.client;
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
import org.springframework.test.web.reactive.server.WebTestClient.Builder;
|
||||
|
||||
/**
|
||||
* A customizer for a {@link Builder}. Any {@code WebTestClientBuilderCustomizer} beans
|
||||
* found in the application context will be {@link #customize called} to customize the
|
||||
* auto-configured {@link Builder}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface WebTestClientBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the given {@code builder}.
|
||||
* @param builder the builder
|
||||
*/
|
||||
void customize(Builder builder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
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.codec.CodecCustomizer;
|
||||
import org.springframework.boot.web.server.reactive.AbstractReactiveWebServerFactory;
|
||||
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.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.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.reactive.function.client.ExchangeStrategies;
|
||||
|
||||
/**
|
||||
* {@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);
|
||||
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(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 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 (AotDetector.useGeneratedArtifacts()) {
|
||||
return;
|
||||
}
|
||||
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) this.beanFactory,
|
||||
WebTestClient.class, false, false).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}.
|
||||
*/
|
||||
public static class WebTestClientFactory implements FactoryBean<WebTestClient>, ApplicationContextAware {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private WebTestClient object;
|
||||
|
||||
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";
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@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() {
|
||||
boolean sslEnabled = isSslEnabled(this.applicationContext);
|
||||
String port = this.applicationContext.getEnvironment().getProperty("local.server.port", "8080");
|
||||
String baseUrl = getBaseUrl(sslEnabled, port);
|
||||
WebTestClient.Builder builder = WebTestClient.bindToServer();
|
||||
customizeWebTestClientBuilder(builder, this.applicationContext);
|
||||
customizeWebTestClientCodecs(builder, this.applicationContext);
|
||||
return builder.baseUrl(baseUrl).build();
|
||||
}
|
||||
|
||||
private String getBaseUrl(boolean sslEnabled, String port) {
|
||||
String basePath = deduceBasePath();
|
||||
String pathSegment = (StringUtils.hasText(basePath)) ? basePath : "";
|
||||
return (sslEnabled ? "https" : "http") + "://localhost:" + port + pathSegment;
|
||||
}
|
||||
|
||||
private String deduceBasePath() {
|
||||
WebApplicationType webApplicationType = deduceFromApplicationContext(this.applicationContext.getClass());
|
||||
if (webApplicationType == WebApplicationType.REACTIVE) {
|
||||
return this.applicationContext.getEnvironment().getProperty("spring.webflux.base-path");
|
||||
}
|
||||
else if (webApplicationType == WebApplicationType.SERVLET) {
|
||||
return ((WebApplicationContext) this.applicationContext).getServletContext().getContextPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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 {
|
||||
AbstractReactiveWebServerFactory webServerFactory = context
|
||||
.getBean(AbstractReactiveWebServerFactory.class);
|
||||
return webServerFactory.getSsl() != null && webServerFactory.getSsl().isEnabled();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeWebTestClientBuilder(WebTestClient.Builder clientBuilder, ApplicationContext context) {
|
||||
for (WebTestClientBuilderCustomizer customizer : context
|
||||
.getBeansOfType(WebTestClientBuilderCustomizer.class)
|
||||
.values()) {
|
||||
customizer.customize(clientBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private void customizeWebTestClientCodecs(WebTestClient.Builder clientBuilder, ApplicationContext context) {
|
||||
Collection<CodecCustomizer> codecCustomizers = context.getBeansOfType(CodecCustomizer.class).values();
|
||||
if (!CollectionUtils.isEmpty(codecCustomizers)) {
|
||||
clientBuilder.exchangeStrategies(ExchangeStrategies.builder()
|
||||
.codecs((codecs) -> codecCustomizers
|
||||
.forEach((codecCustomizer) -> codecCustomizer.customize(codecs)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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 ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
|
||||
SpringBootTest.class);
|
||||
return (springBootTest != null && webClientPresent) ? new WebTestClientContextCustomizer() : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring Boot support for testing Spring WebFlux server endpoints via
|
||||
* {@link org.springframework.test.web.reactive.server.WebTestClient}.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test.client.reactive;
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.htmlunit.FailingHttpStatusCodeException;
|
||||
import org.htmlunit.Page;
|
||||
import org.htmlunit.WebClient;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link WebClient} will automatically prefix relative URLs with
|
||||
* <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class LocalHostWebClient extends WebClient {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public LocalHostWebClient(Environment environment) {
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P extends Page> P getPage(String url) throws IOException, FailingHttpStatusCodeException {
|
||||
if (url.startsWith("/")) {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
url = "http://localhost:" + port + url;
|
||||
}
|
||||
return super.getPage(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* HtmlUnit support classes.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test.htmlunit;
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.htmlunit.webdriver;
|
||||
|
||||
import org.htmlunit.BrowserVersion;
|
||||
import org.openqa.selenium.Capabilities;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.web.servlet.htmlunit.webdriver.WebConnectionHtmlUnitDriver;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link LocalHostWebConnectionHtmlUnitDriver} will automatically prefix relative URLs
|
||||
* with <code>localhost:${local.server.port}</code>.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class LocalHostWebConnectionHtmlUnitDriver extends WebConnectionHtmlUnitDriver {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment) {
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment, boolean enableJavascript) {
|
||||
super(enableJavascript);
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment, BrowserVersion browserVersion) {
|
||||
super(browserVersion);
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public LocalHostWebConnectionHtmlUnitDriver(Environment environment, Capabilities capabilities) {
|
||||
super(capabilities);
|
||||
Assert.notNull(environment, "'environment' must not be null");
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void get(String url) {
|
||||
if (url.startsWith("/")) {
|
||||
String port = this.environment.getProperty("local.server.port", "8080");
|
||||
url = "http://localhost:" + port + url;
|
||||
}
|
||||
super.get(url);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Selenium support classes.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test.htmlunit.webdriver;
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Web server test utilities and support classes.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test;
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.reactor.netty;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to disable the use of global resources in
|
||||
* {@link ReactorResourceFactory} preventing test cleanup issues.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DisableReactorResourceFactoryGlobalResourcesBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ReactorResourceFactory reactorResourceFactory) {
|
||||
reactorResourceFactory.setUseGlobalResources(false);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.reactor.netty;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to disable the use of global resources in
|
||||
* {@link ReactorResourceFactory} preventing test cleanup issues.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
String REACTOR_RESOURCE_FACTORY_CLASS = "org.springframework.http.client.ReactorResourceFactory";
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configAttributes) {
|
||||
if (ClassUtils.isPresent(this.REACTOR_RESOURCE_FACTORY_CLASS, testClass.getClassLoader())) {
|
||||
return new DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
static final class DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer
|
||||
implements ContextCustomizer {
|
||||
|
||||
private DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||
context.getBeanFactory()
|
||||
.registerSingleton(DisableReactorResourceFactoryGlobalResourcesBeanPostProcessor.class.getName(),
|
||||
new DisableReactorResourceFactoryGlobalResourcesBeanPostProcessor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof DisableReactorResourceFactoryGlobalResourcesContextCustomizerCustomizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getClass().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring Boot support for testing Reactor Netty.
|
||||
*/
|
||||
package org.springframework.boot.web.server.test.reactor.netty;
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference
|
||||
import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.RequestEntity
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.client.RestClientException
|
||||
import java.net.URI
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForObject] providing a `getForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForObject(url: String, vararg uriVariables: Any): T? =
|
||||
getForObject(url, T::class.java, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForObject] providing a `getForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForObject(url: String, uriVariables: Map<String, Any?>): T? =
|
||||
getForObject(url, T::class.java, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForObject] providing a `getForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForObject(url: URI): T? =
|
||||
getForObject(url, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForEntity] providing a `getForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForEntity(url: URI): ResponseEntity<T> =
|
||||
getForEntity(url, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForEntity] providing a `getForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForEntity(url: String, vararg uriVariables: Any): ResponseEntity<T> =
|
||||
getForEntity(url, T::class.java, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.getForEntity] providing a `getForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.getForEntity(url: String,
|
||||
uriVariables: Map<String, *>): ResponseEntity<T> =
|
||||
getForEntity(url, T::class.java, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.patchForObject] providing a `patchForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.patchForObject(url: String, request: Any? = null,
|
||||
vararg uriVariables: Any): T? =
|
||||
patchForObject(url, request, T::class.java, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.patchForObject] providing a `patchForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.patchForObject(url: String, request: Any? = null,
|
||||
uriVariables: Map<String, *>): T? =
|
||||
patchForObject(url, request, T::class.java, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.patchForObject] providing a `patchForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.patchForObject(url: URI, request: Any? = null): T? =
|
||||
patchForObject(url, request, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForObject] providing a `postForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForObject(url: String, request: Any? = null,
|
||||
vararg uriVariables: Any): T? =
|
||||
postForObject(url, request, T::class.java, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForObject] providing a `postForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForObject(url: String, request: Any? = null,
|
||||
uriVariables: Map<String, *>): T? =
|
||||
postForObject(url, request, T::class.java, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForObject] providing a `postForObject<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForObject(url: URI, request: Any? = null): T? =
|
||||
postForObject(url, request, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForEntity] providing a `postForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForEntity(url: String, request: Any? = null,
|
||||
vararg uriVariables: Any): ResponseEntity<T> =
|
||||
postForEntity(url, request, T::class.java, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForEntity] providing a `postForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForEntity(url: String, request: Any? = null,
|
||||
uriVariables: Map<String, *>): ResponseEntity<T> =
|
||||
postForEntity(url, request, T::class.java, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.postForEntity] providing a `postForEntity<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. Like the original Java method, this
|
||||
* extension is subject to type erasure. Use [exchange] if you need to retain actual
|
||||
* generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.postForEntity(url: URI, request: Any? = null): ResponseEntity<T> =
|
||||
postForEntity(url, request, T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.exchange] providing an `exchange<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. This extension is not subject to
|
||||
* type erasure and retains actual generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.exchange(url: String, method: HttpMethod,
|
||||
requestEntity: HttpEntity<*>? = null, vararg uriVariables: Any): ResponseEntity<T> =
|
||||
exchange(url, method, requestEntity, object : ParameterizedTypeReference<T>() {}, *uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.exchange] providing an `exchange<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. This extension is not subject to
|
||||
* type erasure and retains actual generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.exchange(url: String, method: HttpMethod,
|
||||
requestEntity: HttpEntity<*>? = null, uriVariables: Map<String, *>): ResponseEntity<T> =
|
||||
exchange(url, method, requestEntity, object : ParameterizedTypeReference<T>() {}, uriVariables)
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.exchange] providing an `exchange<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. This extension is not subject to
|
||||
* type erasure and retains actual generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.exchange(url: URI, method: HttpMethod,
|
||||
requestEntity: HttpEntity<*>? = null): ResponseEntity<T> =
|
||||
exchange(url, method, requestEntity, object : ParameterizedTypeReference<T>() {})
|
||||
|
||||
/**
|
||||
* Extension for [TestRestTemplate.exchange] providing an `exchange<Foo>(...)`
|
||||
* variant leveraging Kotlin reified type parameters. This extension is not subject to
|
||||
* type erasure and retains actual generic type arguments.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Throws(RestClientException::class)
|
||||
inline fun <reified T : Any> TestRestTemplate.exchange(requestEntity: RequestEntity<*>): ResponseEntity<T> =
|
||||
exchange(requestEntity, object : ParameterizedTypeReference<T>() {})
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"groups": [],
|
||||
"properties": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Environment Post Processors
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
org.springframework.boot.web.server.test.SpringBootTestRandomPortEnvironmentPostProcessor
|
||||
|
||||
# Spring Test Context Customizer Factories
|
||||
org.springframework.test.context.ContextCustomizerFactory=\
|
||||
org.springframework.boot.web.server.test.client.TestRestTemplateContextCustomizerFactory,\
|
||||
org.springframework.boot.web.server.test.client.reactive.WebTestClientContextCustomizerFactory,\
|
||||
org.springframework.boot.web.server.test.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
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.web.server.test.client.TestRestTemplate;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base class for {@link SpringBootTest @SpringBootTest} tests configured to start an
|
||||
* embedded reactive container.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
abstract class AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Value("${value}")
|
||||
private int value = 0;
|
||||
|
||||
@Autowired
|
||||
private ReactiveWebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webClient;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
ReactiveWebApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@Test
|
||||
void runAndTestHttpEndpoint() {
|
||||
assertThat(this.port).isNotEqualTo(8080).isNotZero();
|
||||
WebTestClient.bindToServer()
|
||||
.baseUrl("http://localhost:" + this.port)
|
||||
.responseTimeout(Duration.ofMinutes(5))
|
||||
.build()
|
||||
.get()
|
||||
.uri("/")
|
||||
.exchange()
|
||||
.expectBody(String.class)
|
||||
.isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectWebTestClient() {
|
||||
this.webClient.get().uri("/").exchange().expectBody(String.class).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectTestRestTemplate() {
|
||||
String body = this.restTemplate.getForObject("/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void annotationAttributesOverridePropertiesFile() {
|
||||
assertThat(this.value).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AbstractConfig {
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Bean
|
||||
HttpHandler httpHandler(ApplicationContext applicationContext) {
|
||||
return WebHttpHandlerBuilder.applicationContext(applicationContext).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveWebServerFactory webServerFactory() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
factory.setPort(this.port);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
Mono<String> home() {
|
||||
return Mono.just("Hello World");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.test.client.TestRestTemplate;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Base class for {@link SpringBootTest @SpringBootTest} tests configured to start an
|
||||
* embedded web server.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
abstract class AbstractSpringBootTestWebServerWebEnvironmentTests {
|
||||
|
||||
@LocalServerPort
|
||||
private int port = 0;
|
||||
|
||||
@Value("${value}")
|
||||
private int value = 0;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Autowired
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
WebApplicationContext getContext() {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
TestRestTemplate getRestTemplate() {
|
||||
return this.restTemplate;
|
||||
}
|
||||
|
||||
@Test
|
||||
void runAndTestHttpEndpoint() {
|
||||
assertThat(this.port).isNotEqualTo(8080).isNotZero();
|
||||
String body = new RestTemplate().getForObject("http://localhost:" + this.port + "/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectTestRestTemplate() {
|
||||
String body = this.restTemplate.getForObject("/", String.class);
|
||||
assertThat(body).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
void annotationAttributesOverridePropertiesFile() {
|
||||
assertThat(this.value).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateWebApplicationContextIsSet() {
|
||||
assertThat(this.context).isSameAs(WebApplicationContextUtils.getWebApplicationContext(this.servletContext));
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class AbstractConfig {
|
||||
|
||||
@Value("${server.port:8080}")
|
||||
private int port = 8080;
|
||||
|
||||
@Bean
|
||||
DispatcherServlet dispatcherServlet() {
|
||||
return new DispatcherServlet();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServletWebServerFactory webServerFactory() {
|
||||
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
|
||||
factory.setPort(this.port);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||
return new PropertySourcesPlaceholderConfigurer();
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
String home() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalManagementPort @LocalManagementPort}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@TestPropertySource(properties = "local.management.port=8181")
|
||||
class LocalManagementPortTests {
|
||||
|
||||
@Value("${local.management.port}")
|
||||
private String fromValue;
|
||||
|
||||
@LocalManagementPort
|
||||
private String fromAnnotation;
|
||||
|
||||
@Test
|
||||
void testLocalManagementPortAnnotation() {
|
||||
assertThat(this.fromAnnotation).isNotNull().isEqualTo(this.fromValue);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalServerPort @LocalServerPort}.
|
||||
*
|
||||
* @author Anand Shah
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@TestPropertySource(properties = "local.server.port=8181")
|
||||
class LocalServerPortTests {
|
||||
|
||||
@Value("${local.server.port}")
|
||||
private String fromValue;
|
||||
|
||||
@LocalServerPort
|
||||
private String fromAnnotation;
|
||||
|
||||
@Test
|
||||
void testLocalServerPortAnnotation() {
|
||||
assertThat(this.fromAnnotation).isNotNull().isEqualTo(this.fromValue);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class Config {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.test.context.support.TestPropertySourceUtils;
|
||||
import org.springframework.util.PlaceholderResolutionException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTestRandomPortEnvironmentPostProcessor}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class SpringBootTestRandomPortEnvironmentPostProcessorTests {
|
||||
|
||||
private final SpringBootTestRandomPortEnvironmentPostProcessor postProcessor = new SpringBootTestRandomPortEnvironmentPostProcessor();
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
private MutablePropertySources propertySources;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.propertySources = this.environment.getPropertySources();
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenServerAndManagementPortIsZeroInTestPropertySource() {
|
||||
addTestPropertySource("0", "0");
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenServerPortAndManagementPortIsZeroInDifferentPropertySources() {
|
||||
addTestPropertySource("0", null);
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("management.server.port", "0");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerAndTestManagementPortAreNonZero() {
|
||||
addTestPropertySource("8080", "8081");
|
||||
this.environment.setProperty("server.port", "8080");
|
||||
this.environment.setProperty("management.server.port", "8081");
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("8080");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8081");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndTestManagementPortIsNotNull() {
|
||||
addTestPropertySource("0", "8080");
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("8080");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsNull() {
|
||||
addTestPropertySource("0", null);
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndSameInProduction() {
|
||||
addTestPropertySource("0", null);
|
||||
Map<String, Object> other = new HashMap<>();
|
||||
other.put("server.port", "8081");
|
||||
other.put("management.server.port", "8081");
|
||||
MapPropertySource otherSource = new MapPropertySource("other", other);
|
||||
this.propertySources.addLast(otherSource);
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndDefaultSameInProduction() {
|
||||
// mgmt port is 8080 which means it's on the same port as main server since that
|
||||
// is null in app properties
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8080")));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndDifferentInProduction() {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "8081")));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortMinusOne() {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "-1")));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("-1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenTestServerPortIsZeroAndManagementPortIsAnInteger() {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", 8081)));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenManagementServerPortPlaceholderPresentShouldResolvePlaceholder() {
|
||||
addTestPropertySource("0", null);
|
||||
MapPropertySource testPropertySource = (MapPropertySource) this.propertySources
|
||||
.get(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
testPropertySource.getSource().put("port", "9090");
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}")));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenManagementServerPortPlaceholderAbsentShouldFail() {
|
||||
addTestPropertySource("0", null);
|
||||
this.propertySources
|
||||
.addLast(new MapPropertySource("other", Collections.singletonMap("management.server.port", "${port}")));
|
||||
assertThatExceptionOfType(PlaceholderResolutionException.class)
|
||||
.isThrownBy(() -> this.postProcessor.postProcessEnvironment(this.environment, null))
|
||||
.withMessage("Could not resolve placeholder 'port' in value \"${port}\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenServerPortPlaceholderPresentShouldResolvePlaceholder() {
|
||||
addTestPropertySource("0", null);
|
||||
MapPropertySource testPropertySource = (MapPropertySource) this.propertySources
|
||||
.get(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
|
||||
testPropertySource.getSource().put("port", "8080");
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("server.port", "${port}");
|
||||
source.put("management.server.port", "9090");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
this.postProcessor.postProcessEnvironment(this.environment, null);
|
||||
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
|
||||
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void postProcessWhenServerPortPlaceholderAbsentShouldFail() {
|
||||
addTestPropertySource("0", null);
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("server.port", "${port}");
|
||||
source.put("management.server.port", "9090");
|
||||
this.propertySources.addLast(new MapPropertySource("other", source));
|
||||
assertThatExceptionOfType(PlaceholderResolutionException.class)
|
||||
.isThrownBy(() -> this.postProcessor.postProcessEnvironment(this.environment, null))
|
||||
.withMessage("Could not resolve placeholder 'port' in value \"${port}\"");
|
||||
}
|
||||
|
||||
private void addTestPropertySource(String serverPort, String managementPort) {
|
||||
Map<String, Object> source = new HashMap<>();
|
||||
source.put("server.port", serverPort);
|
||||
source.put("management.server.port", managementPort);
|
||||
MapPropertySource inlineTestSource = new MapPropertySource(
|
||||
TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, source);
|
||||
this.propertySources.addFirst(inlineTestSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} in a reactive environment configured
|
||||
* with {@link WebEnvironment#DEFINED_PORT}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT,
|
||||
properties = { "spring.main.web-application-type=reactive", "server.port=0", "value=123" })
|
||||
public class SpringBootTestReactiveWebEnvironmentDefinedPortTests
|
||||
extends AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebFlux
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} in a reactive environment configured
|
||||
* with {@link WebEnvironment#RANDOM_PORT}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.main.webApplicationType=reactive", "value=123" })
|
||||
public class SpringBootTestReactiveWebEnvironmentRandomPortTests
|
||||
extends AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebFlux
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.reactive.config.EnableWebFlux;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} in a reactive environment configured
|
||||
* with a user-defined {@link RestTemplate} that is named {@code testRestTemplate}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||
properties = { "spring.main.web-application-type=reactive", "value=123" })
|
||||
class SpringBootTestReactiveWebEnvironmentUserDefinedTestRestTemplateTests
|
||||
extends AbstractSpringBootTestEmbeddedReactiveWebEnvironmentTests {
|
||||
|
||||
@Test
|
||||
void restTemplateIsUserDefined() {
|
||||
assertThat(getContext().getBean("testRestTemplate")).isInstanceOf(RestTemplate.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebFlux
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
@Bean
|
||||
RestTemplate testRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} configured with a user-defined
|
||||
* {@link RestTemplate} that is named {@code testRestTemplate}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "value=123" })
|
||||
class SpringBootTestUserDefinedTestRestTemplateTests extends AbstractSpringBootTestWebServerWebEnvironmentTests {
|
||||
|
||||
@Test
|
||||
void restTemplateIsUserDefined() {
|
||||
assertThat(getContext().getBean("testRestTemplate")).isInstanceOf(RestTemplate.class);
|
||||
}
|
||||
|
||||
// gh-7711
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
@Bean
|
||||
RestTemplate testRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
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.web.server.test.AbstractSpringBootTestWebServerWebEnvironmentTests.AbstractConfig;
|
||||
import org.springframework.boot.web.server.test.SpringBootTestWebEnvironmentContextHierarchyTests.ChildConfiguration;
|
||||
import org.springframework.boot.web.server.test.SpringBootTestWebEnvironmentContextHierarchyTests.ParentConfiguration;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.ContextHierarchy;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} configured with
|
||||
* {@link WebEnvironment#DEFINED_PORT}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = { "server.port=0", "value=123" })
|
||||
@ContextHierarchy({ @ContextConfiguration(classes = ParentConfiguration.class),
|
||||
@ContextConfiguration(classes = ChildConfiguration.class) })
|
||||
class SpringBootTestWebEnvironmentContextHierarchyTests {
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@Test
|
||||
void testShouldOnlyStartSingleServer() {
|
||||
ApplicationContext parent = this.context.getParent();
|
||||
assertThat(this.context).isInstanceOf(WebApplicationContext.class);
|
||||
assertThat(parent).isNotInstanceOf(WebApplicationContext.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ParentConfiguration extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
@RestController
|
||||
static class ChildConfiguration extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} configured with
|
||||
* {@link WebEnvironment#DEFINED_PORT}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT, properties = { "server.port=0", "value=123" })
|
||||
class SpringBootTestWebEnvironmentDefinedPortTests extends AbstractSpringBootTestWebServerWebEnvironmentTests {
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
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.web.server.test.AbstractSpringBootTestWebServerWebEnvironmentTests.AbstractConfig;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for {@link SpringBootTest @SpringBootTest} with a custom inline server.port in an
|
||||
* embedded web environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "server.port=12345" })
|
||||
class SpringBootTestWebEnvironmentRandomPortCustomPortTests {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Test
|
||||
void validatePortIsNotOverwritten() {
|
||||
String port = this.environment.getProperty("server.port");
|
||||
assertThat(port).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringBootTest @SpringBootTest} configured with
|
||||
* {@link WebEnvironment#RANDOM_PORT}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@DirtiesContext
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { "value=123" })
|
||||
class SpringBootTestWebEnvironmentRandomPortTests extends AbstractSpringBootTestWebServerWebEnvironmentTests {
|
||||
|
||||
@Test
|
||||
void testRestTemplateShouldUseBuilder() {
|
||||
assertThat(getRestTemplate().getRestTemplate().getMessageConverters())
|
||||
.hasAtLeastOneElementOfType(MyConverter.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebMvc
|
||||
@RestController
|
||||
static class Config extends AbstractConfig {
|
||||
|
||||
@Bean
|
||||
RestTemplateBuilder restTemplateBuilder() {
|
||||
return new RestTemplateBuilder().additionalMessageConverters(new MyConverter());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyConverter extends StringHttpMessageConverter {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.web.util.UriTemplateHandler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalHostUriTemplateHandler}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
class LocalHostUriTemplateHandlerTests {
|
||||
|
||||
@Test
|
||||
void createWhenEnvironmentIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new LocalHostUriTemplateHandler(null))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenSchemeIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new LocalHostUriTemplateHandler(new MockEnvironment(), null))
|
||||
.withMessageContaining("'scheme' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenHandlerIsNullShouldThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new LocalHostUriTemplateHandler(new MockEnvironment(), "http", null))
|
||||
.withMessageContaining("'handler' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriShouldUseLocalServerPort() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("local.server.port", "1234");
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(environment);
|
||||
assertThat(handler.getRootUri()).isEqualTo("http://localhost:1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriWhenLocalServerPortMissingShouldUsePort8080() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(environment);
|
||||
assertThat(handler.getRootUri()).isEqualTo("http://localhost:8080");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriUsesCustomScheme() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(environment, "https");
|
||||
assertThat(handler.getRootUri()).isEqualTo("https://localhost:8080");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriShouldUseContextPath() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("server.servlet.context-path", "/foo");
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(environment);
|
||||
assertThat(handler.getRootUri()).isEqualTo("http://localhost:8080/foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void expandShouldUseCustomHandler() {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
UriTemplateHandler uriTemplateHandler = mock(UriTemplateHandler.class);
|
||||
Map<String, ?> uriVariables = new HashMap<>();
|
||||
URI uri = URI.create("https://www.example.com");
|
||||
given(uriTemplateHandler.expand("https://localhost:8080/", uriVariables)).willReturn(uri);
|
||||
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(environment, "https", uriTemplateHandler);
|
||||
assertThat(handler.expand("/", uriVariables)).isEqualTo(uri);
|
||||
then(uriTemplateHandler).should().expand("https://localhost:8080/", uriVariables);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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.web.server.test.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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to check no {@link TestRestTemplate} definition is registered
|
||||
* when config classes are processed.
|
||||
*/
|
||||
class NoTestRestTemplateBeanChecker implements ImportSelector, BeanFactoryAware {
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) {
|
||||
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) beanFactory,
|
||||
TestRestTemplate.class))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import jakarta.servlet.GenericServlet;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
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.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.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestRestTemplateContextCustomizer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
class TestRestTemplateContextCustomizerIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertThat(this.restTemplate.getForObject("/", String.class)).contains("hello");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ TestServlet.class, NoTestRestTemplateBeanChecker.class })
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
TomcatServletWebServerFactory webServerFactory() {
|
||||
return new TomcatServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestServlet extends GenericServlet {
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
try (PrintWriter writer = response.getWriter()) {
|
||||
writer.println("hello");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-2023 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.web.server.test.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.web.server.test.client.TestRestTemplateContextCustomizer.TestRestTemplateRegistrar;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.AbstractApplicationContext;
|
||||
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 {@link TestRestTemplateContextCustomizer}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class TestRestTemplateContextCustomizerTests {
|
||||
|
||||
@Test
|
||||
void whenContextIsNotABeanDefinitionRegistryTestRestTemplateIsRegistered() {
|
||||
new ApplicationContextRunner(TestApplicationContext::new)
|
||||
.withInitializer(this::applyTestRestTemplateContextCustomizer)
|
||||
.run((context) -> assertThat(context).hasSingleBean(TestRestTemplate.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingAotGeneratedArtifactsTestRestTemplateIsNotRegistered() {
|
||||
new ApplicationContextRunner().withSystemProperties("spring.aot.enabled:true")
|
||||
.withInitializer(this::applyTestRestTemplateContextCustomizer)
|
||||
.run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(TestRestTemplateRegistrar.class);
|
||||
assertThat(context).doesNotHaveBean(TestRestTemplate.class);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
void applyTestRestTemplateContextCustomizer(ConfigurableApplicationContext context) {
|
||||
MergedContextConfiguration configuration = mock(MergedContextConfiguration.class);
|
||||
given(configuration.getTestClass()).willReturn((Class) TestClass.class);
|
||||
new TestRestTemplateContextCustomizer().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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.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.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestRestTemplateContextCustomizer} to ensure
|
||||
* early-initialization of factory beans doesn't occur.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@SpringBootTest(classes = TestRestTemplateContextCustomizerWithFactoryBeanTests.TestClassWithFactoryBean.class,
|
||||
webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
class TestRestTemplateContextCustomizerWithFactoryBeanTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertThat(this.restTemplate).isNotNull();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ComponentScan("org.springframework.boot.test.web.client.scan")
|
||||
static class TestClassWithFactoryBean {
|
||||
|
||||
@Bean
|
||||
TomcatServletWebServerFactory webServerFactory() {
|
||||
return new TomcatServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import jakarta.servlet.GenericServlet;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
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.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.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestRestTemplateContextCustomizer} with a custom
|
||||
* {@link TestRestTemplate} bean.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||
@DirtiesContext
|
||||
class TestRestTemplateContextCustomizerWithOverrideIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
private TestRestTemplate restTemplate;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertThat(this.restTemplate).isInstanceOf(CustomTestRestTemplate.class);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Import({ TestServlet.class, NoTestRestTemplateBeanChecker.class })
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
TomcatServletWebServerFactory webServerFactory() {
|
||||
return new TomcatServletWebServerFactory(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
TestRestTemplate template() {
|
||||
return new CustomTestRestTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestServlet extends GenericServlet {
|
||||
|
||||
@Override
|
||||
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
|
||||
try (PrintWriter writer = response.getWriter()) {
|
||||
writer.println("hello");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomTestRestTemplate extends TestRestTemplate {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Redirect;
|
||||
import java.util.Base64;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.hc.client5.http.config.RequestConfig;
|
||||
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
|
||||
import org.apache.hc.client5.http.impl.classic.RedirectExec;
|
||||
import org.apache.hc.client5.http.protocol.RedirectStrategy;
|
||||
import org.assertj.core.extractor.Extractors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
|
||||
import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
|
||||
import org.springframework.boot.http.client.HttpRedirects;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.server.test.client.TestRestTemplate.HttpClientOption;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.JdkClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockClientHttpResponse;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.web.client.NoOpResponseErrorHandler;
|
||||
import org.springframework.web.client.ResponseErrorHandler;
|
||||
import org.springframework.web.client.RestOperations;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.DefaultUriBuilderFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestRestTemplate}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Kristine Jetzke
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
class TestRestTemplateTests {
|
||||
|
||||
@Test
|
||||
void fromRestTemplateBuilder() {
|
||||
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
|
||||
RestTemplate delegate = new RestTemplate();
|
||||
given(builder.build()).willReturn(delegate);
|
||||
assertThat(new TestRestTemplate(builder).getRestTemplate()).isEqualTo(delegate);
|
||||
}
|
||||
|
||||
@Test
|
||||
void simple() {
|
||||
// The Apache client is on the classpath so we get the fully-fledged factory
|
||||
assertThat(new TestRestTemplate().getRestTemplate().getRequestFactory())
|
||||
.isInstanceOf(HttpComponentsClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doNotReplaceCustomRequestFactory() {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(TestClientHttpRequestFactory.class);
|
||||
TestRestTemplate testRestTemplate = new TestRestTemplate(builder);
|
||||
assertThat(testRestTemplate.getRestTemplate().getRequestFactory())
|
||||
.isInstanceOf(TestClientHttpRequestFactory.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void useTheSameRequestFactoryClassWithBasicAuth() {
|
||||
TestClientHttpRequestFactory customFactory = new TestClientHttpRequestFactory();
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder().requestFactory(() -> customFactory);
|
||||
TestRestTemplate testRestTemplate = new TestRestTemplate(builder).withBasicAuth("test", "test");
|
||||
RestTemplate restTemplate = testRestTemplate.getRestTemplate();
|
||||
assertThat(restTemplate.getRequestFactory()).isEqualTo(customFactory).hasSameClassAs(customFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriRootUriSetViaRestTemplateBuilder() {
|
||||
String rootUri = "https://example.com";
|
||||
RestTemplateBuilder delegate = new RestTemplateBuilder().rootUri(rootUri);
|
||||
assertThat(new TestRestTemplate(delegate).getRootUri()).isEqualTo(rootUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriRootUriSetViaLocalHostUriTemplateHandler() {
|
||||
String rootUri = "https://example.com";
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
LocalHostUriTemplateHandler templateHandler = mock(LocalHostUriTemplateHandler.class);
|
||||
given(templateHandler.getRootUri()).willReturn(rootUri);
|
||||
template.setUriTemplateHandler(templateHandler);
|
||||
assertThat(template.getRootUri()).isEqualTo(rootUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRootUriRootUriNotSet() {
|
||||
assertThat(new TestRestTemplate().getRootUri()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void authenticated() {
|
||||
TestRestTemplate restTemplate = new TestRestTemplate("user", "password");
|
||||
assertBasicAuthorizationCredentials(restTemplate, "user", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("removal")
|
||||
void options() {
|
||||
RequestConfig config = getRequestConfig(
|
||||
new TestRestTemplate(HttpClientOption.ENABLE_REDIRECTS, HttpClientOption.ENABLE_COOKIES));
|
||||
assertThat(config.isRedirectsEnabled()).isTrue();
|
||||
assertThat(config.getCookieSpec()).isEqualTo("strict");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdkBuilderCanBeSpecifiedWithSpecificRedirects() {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder()
|
||||
.requestFactoryBuilder(ClientHttpRequestFactoryBuilder.jdk());
|
||||
TestRestTemplate templateWithRedirects = new TestRestTemplate(builder.redirects(HttpRedirects.FOLLOW));
|
||||
assertThat(getJdkHttpClient(templateWithRedirects).followRedirects()).isEqualTo(Redirect.NORMAL);
|
||||
TestRestTemplate templateWithoutRedirects = new TestRestTemplate(builder.redirects(HttpRedirects.DONT_FOLLOW));
|
||||
assertThat(getJdkHttpClient(templateWithoutRedirects).followRedirects()).isEqualTo(Redirect.NEVER);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("removal")
|
||||
void httpComponentsAreBuiltConsideringSettingsInRestTemplateBuilder() {
|
||||
RestTemplateBuilder builder = new RestTemplateBuilder()
|
||||
.requestFactoryBuilder(ClientHttpRequestFactoryBuilder.httpComponents());
|
||||
assertThat(getRedirectStrategy((RestTemplateBuilder) null)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(null, HttpClientOption.ENABLE_REDIRECTS)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(builder)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(builder, HttpClientOption.ENABLE_REDIRECTS)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(builder.redirects(HttpRedirects.DONT_FOLLOW)))
|
||||
.matches(this::isDontFollowStrategy);
|
||||
assertThat(getRedirectStrategy(builder.redirects(HttpRedirects.DONT_FOLLOW), HttpClientOption.ENABLE_REDIRECTS))
|
||||
.matches(this::isFollowStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRequestFactorySettingsRedirectsForHttpComponents() {
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
assertThat(getRedirectStrategy(template)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(template.withRequestFactorySettings(
|
||||
ClientHttpRequestFactorySettings.defaults().withRedirects(HttpRedirects.FOLLOW))))
|
||||
.matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(template.withRequestFactorySettings(
|
||||
ClientHttpRequestFactorySettings.defaults().withRedirects(HttpRedirects.DONT_FOLLOW))))
|
||||
.matches(this::isDontFollowStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRedirects() {
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
assertThat(getRedirectStrategy(template)).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(template.withRedirects(HttpRedirects.FOLLOW))).matches(this::isFollowStrategy);
|
||||
assertThat(getRedirectStrategy(template.withRedirects(HttpRedirects.DONT_FOLLOW)))
|
||||
.matches(this::isDontFollowStrategy);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRequestFactorySettingsRedirectsForJdk() {
|
||||
TestRestTemplate template = new TestRestTemplate(
|
||||
new RestTemplateBuilder().requestFactoryBuilder(ClientHttpRequestFactoryBuilder.jdk()));
|
||||
assertThat(getJdkHttpClient(template).followRedirects()).isEqualTo(Redirect.NORMAL);
|
||||
assertThat(getJdkHttpClient(template.withRequestFactorySettings(
|
||||
ClientHttpRequestFactorySettings.defaults().withRedirects(HttpRedirects.DONT_FOLLOW)))
|
||||
.followRedirects()).isEqualTo(Redirect.NEVER);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withRequestFactorySettingsUpdateRedirectsForJdk() {
|
||||
TestRestTemplate template = new TestRestTemplate(
|
||||
new RestTemplateBuilder().requestFactoryBuilder(ClientHttpRequestFactoryBuilder.jdk()));
|
||||
assertThat(getJdkHttpClient(template).followRedirects()).isEqualTo(Redirect.NORMAL);
|
||||
assertThat(getJdkHttpClient(
|
||||
template.withRequestFactorySettings((settings) -> settings.withRedirects(HttpRedirects.DONT_FOLLOW)))
|
||||
.followRedirects()).isEqualTo(Redirect.NEVER);
|
||||
}
|
||||
|
||||
private RequestConfig getRequestConfig(TestRestTemplate template) {
|
||||
ClientHttpRequestFactory requestFactory = template.getRestTemplate().getRequestFactory();
|
||||
return (RequestConfig) Extractors.byName("httpClient.defaultConfig").apply(requestFactory);
|
||||
}
|
||||
|
||||
private RedirectStrategy getRedirectStrategy(RestTemplateBuilder builder, HttpClientOption... httpClientOptions) {
|
||||
builder = (builder != null) ? builder : new RestTemplateBuilder();
|
||||
TestRestTemplate template = new TestRestTemplate(builder, null, null, httpClientOptions);
|
||||
return getRedirectStrategy(template);
|
||||
}
|
||||
|
||||
private RedirectStrategy getRedirectStrategy(TestRestTemplate template) {
|
||||
ClientHttpRequestFactory requestFactory = template.getRestTemplate().getRequestFactory();
|
||||
Object chain = Extractors.byName("httpClient.execChain").apply(requestFactory);
|
||||
while (chain != null) {
|
||||
Object handler = Extractors.byName("handler").apply(chain);
|
||||
if (handler instanceof RedirectExec) {
|
||||
return (RedirectStrategy) Extractors.byName("redirectStrategy").apply(handler);
|
||||
}
|
||||
chain = Extractors.byName("next").apply(chain);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isFollowStrategy(RedirectStrategy redirectStrategy) {
|
||||
return redirectStrategy instanceof DefaultRedirectStrategy;
|
||||
}
|
||||
|
||||
private boolean isDontFollowStrategy(RedirectStrategy redirectStrategy) {
|
||||
return redirectStrategy.getClass().getName().contains("NoFollow");
|
||||
}
|
||||
|
||||
private HttpClient getJdkHttpClient(TestRestTemplate template) {
|
||||
JdkClientHttpRequestFactory requestFactory = (JdkClientHttpRequestFactory) template.getRestTemplate()
|
||||
.getRequestFactory();
|
||||
return (HttpClient) ReflectionTestUtils.getField(requestFactory, "httpClient");
|
||||
}
|
||||
|
||||
@Test
|
||||
void restOperationsAreAvailable() {
|
||||
RestTemplate delegate = mock(RestTemplate.class);
|
||||
given(delegate.getRequestFactory()).willReturn(new SimpleClientHttpRequestFactory());
|
||||
given(delegate.getUriTemplateHandler()).willReturn(new DefaultUriBuilderFactory());
|
||||
RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
|
||||
given(builder.build()).willReturn(delegate);
|
||||
TestRestTemplate restTemplate = new TestRestTemplate(builder);
|
||||
ReflectionUtils.doWithMethods(RestOperations.class, new MethodCallback() {
|
||||
|
||||
@Override
|
||||
public void doWith(Method method) {
|
||||
Method equivalent = ReflectionUtils.findMethod(TestRestTemplate.class, method.getName(),
|
||||
method.getParameterTypes());
|
||||
assertThat(equivalent).as("Method %s not found", method).isNotNull();
|
||||
assertThat(Modifier.isPublic(equivalent.getModifiers()))
|
||||
.as("Method %s should have been public", equivalent)
|
||||
.isTrue();
|
||||
try {
|
||||
equivalent.invoke(restTemplate, mockArguments(method.getParameterTypes()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] mockArguments(Class<?>[] parameterTypes) throws Exception {
|
||||
Object[] arguments = new Object[parameterTypes.length];
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
arguments[i] = mockArgument(parameterTypes[i]);
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Object mockArgument(Class<?> type) throws Exception {
|
||||
if (String.class.equals(type)) {
|
||||
return "String";
|
||||
}
|
||||
if (Object[].class.equals(type)) {
|
||||
return new Object[0];
|
||||
}
|
||||
if (URI.class.equals(type)) {
|
||||
return new URI("http://localhost");
|
||||
}
|
||||
if (HttpMethod.class.equals(type)) {
|
||||
return HttpMethod.GET;
|
||||
}
|
||||
if (Class.class.equals(type)) {
|
||||
return Object.class;
|
||||
}
|
||||
if (RequestEntity.class.equals(type)) {
|
||||
return new RequestEntity(HttpMethod.GET, new URI("http://localhost"));
|
||||
}
|
||||
return mock(type);
|
||||
}
|
||||
|
||||
}, (method) -> Modifier.isPublic(method.getModifiers()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBasicAuthAddsBasicAuthWhenNotAlreadyPresent() {
|
||||
TestRestTemplate original = new TestRestTemplate();
|
||||
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
|
||||
assertThat(getConverterClasses(original)).containsExactlyElementsOf(getConverterClasses(basicAuth).toList());
|
||||
assertThat(basicAuth.getRestTemplate().getInterceptors()).isEmpty();
|
||||
assertBasicAuthorizationCredentials(original, null, null);
|
||||
assertBasicAuthorizationCredentials(basicAuth, "user", "password");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBasicAuthReplacesBasicAuthWhenAlreadyPresent() {
|
||||
TestRestTemplate original = new TestRestTemplate("foo", "bar").withBasicAuth("replace", "replace");
|
||||
TestRestTemplate basicAuth = original.withBasicAuth("user", "password");
|
||||
assertThat(getConverterClasses(basicAuth)).containsExactlyElementsOf(getConverterClasses(original).toList());
|
||||
assertBasicAuthorizationCredentials(original, "replace", "replace");
|
||||
assertBasicAuthorizationCredentials(basicAuth, "user", "password");
|
||||
}
|
||||
|
||||
private Stream<Class<?>> getConverterClasses(TestRestTemplate testRestTemplate) {
|
||||
return testRestTemplate.getRestTemplate().getMessageConverters().stream().map(Object::getClass);
|
||||
}
|
||||
|
||||
@Test
|
||||
void withBasicAuthShouldUseNoOpErrorHandler() {
|
||||
TestRestTemplate originalTemplate = new TestRestTemplate("foo", "bar");
|
||||
ResponseErrorHandler errorHandler = mock(ResponseErrorHandler.class);
|
||||
originalTemplate.getRestTemplate().setErrorHandler(errorHandler);
|
||||
TestRestTemplate basicAuthTemplate = originalTemplate.withBasicAuth("user", "password");
|
||||
assertThat(basicAuthTemplate.getRestTemplate().getErrorHandler()).isInstanceOf(NoOpResponseErrorHandler.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeWithRelativeTemplatedUrlRequestEntity() throws Exception {
|
||||
RequestEntity<Void> entity = RequestEntity.get("/a/b/c.{ext}", "txt").build();
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
|
||||
MockClientHttpRequest request = new MockClientHttpRequest();
|
||||
request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
|
||||
URI absoluteUri = URI.create("http://localhost:8080/a/b/c.txt");
|
||||
given(requestFactory.createRequest(eq(absoluteUri), eq(HttpMethod.GET))).willReturn(request);
|
||||
template.getRestTemplate().setRequestFactory(requestFactory);
|
||||
LocalHostUriTemplateHandler uriTemplateHandler = new LocalHostUriTemplateHandler(new MockEnvironment());
|
||||
template.setUriTemplateHandler(uriTemplateHandler);
|
||||
template.exchange(entity, String.class);
|
||||
then(requestFactory).should().createRequest(eq(absoluteUri), eq(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeWithAbsoluteTemplatedUrlRequestEntity() throws Exception {
|
||||
RequestEntity<Void> entity = RequestEntity.get("https://api.example.com/a/b/c.{ext}", "txt").build();
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
|
||||
MockClientHttpRequest request = new MockClientHttpRequest();
|
||||
request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
|
||||
URI absoluteUri = URI.create("https://api.example.com/a/b/c.txt");
|
||||
given(requestFactory.createRequest(eq(absoluteUri), eq(HttpMethod.GET))).willReturn(request);
|
||||
template.getRestTemplate().setRequestFactory(requestFactory);
|
||||
template.exchange(entity, String.class);
|
||||
then(requestFactory).should().createRequest(eq(absoluteUri), eq(HttpMethod.GET));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(TestRestTemplate::delete);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeWithRequestEntityAndClassHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling((testRestTemplate, relativeUri) -> testRestTemplate
|
||||
.exchange(new RequestEntity<>(HttpMethod.GET, relativeUri), String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeWithRequestEntityAndParameterizedTypeReferenceHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling((testRestTemplate, relativeUri) -> testRestTemplate
|
||||
.exchange(new RequestEntity<>(HttpMethod.GET, relativeUri), new ParameterizedTypeReference<String>() {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling((testRestTemplate, relativeUri) -> testRestTemplate.exchange(relativeUri,
|
||||
HttpMethod.GET, new HttpEntity<>(new byte[0]), String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void exchangeWithParameterizedTypeReferenceHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling((testRestTemplate, relativeUri) -> testRestTemplate.exchange(relativeUri,
|
||||
HttpMethod.GET, new HttpEntity<>(new byte[0]), new ParameterizedTypeReference<String>() {
|
||||
}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void executeHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.execute(relativeUri, HttpMethod.GET, null, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getForEntityHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.getForEntity(relativeUri, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getForObjectHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.getForObject(relativeUri, String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void headForHeadersHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(TestRestTemplate::headForHeaders);
|
||||
}
|
||||
|
||||
@Test
|
||||
void optionsForAllowHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(TestRestTemplate::optionsForAllow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void patchForObjectHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.patchForObject(relativeUri, "hello", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void postForEntityHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.postForEntity(relativeUri, "hello", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void postForLocationHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.postForLocation(relativeUri, "hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void postForObjectHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling(
|
||||
(testRestTemplate, relativeUri) -> testRestTemplate.postForObject(relativeUri, "hello", String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void putHandlesRelativeUris() throws IOException {
|
||||
verifyRelativeUriHandling((testRestTemplate, relativeUri) -> testRestTemplate.put(relativeUri, "hello"));
|
||||
}
|
||||
|
||||
private void verifyRelativeUriHandling(TestRestTemplateCallback callback) throws IOException {
|
||||
ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class);
|
||||
MockClientHttpRequest request = new MockClientHttpRequest();
|
||||
request.setResponse(new MockClientHttpResponse(new byte[0], HttpStatus.OK));
|
||||
URI absoluteUri = URI.create("http://localhost:8080/a/b/c.txt?param=%7Bsomething%7D");
|
||||
given(requestFactory.createRequest(eq(absoluteUri), any(HttpMethod.class))).willReturn(request);
|
||||
TestRestTemplate template = new TestRestTemplate();
|
||||
template.getRestTemplate().setRequestFactory(requestFactory);
|
||||
LocalHostUriTemplateHandler uriTemplateHandler = new LocalHostUriTemplateHandler(new MockEnvironment());
|
||||
template.setUriTemplateHandler(uriTemplateHandler);
|
||||
callback.doWithTestRestTemplate(template, URI.create("/a/b/c.txt?param=%7Bsomething%7D"));
|
||||
then(requestFactory).should().createRequest(eq(absoluteUri), any(HttpMethod.class));
|
||||
}
|
||||
|
||||
private void assertBasicAuthorizationCredentials(TestRestTemplate testRestTemplate, String username,
|
||||
String password) {
|
||||
ClientHttpRequest request = ReflectionTestUtils.invokeMethod(testRestTemplate.getRestTemplate(),
|
||||
"createRequest", URI.create("http://localhost"), HttpMethod.POST);
|
||||
if (username == null) {
|
||||
assertThat(request.getHeaders().headerNames()).doesNotContain(HttpHeaders.AUTHORIZATION);
|
||||
}
|
||||
else {
|
||||
assertThat(request.getHeaders().headerNames()).contains(HttpHeaders.AUTHORIZATION);
|
||||
assertThat(request.getHeaders().get(HttpHeaders.AUTHORIZATION)).containsExactly("Basic "
|
||||
+ Base64.getEncoder().encodeToString(String.format("%s:%s", username, password).getBytes()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface TestRestTemplateCallback {
|
||||
|
||||
void doWithTestRestTemplate(TestRestTemplate testRestTemplate, URI relativeUri);
|
||||
|
||||
}
|
||||
|
||||
static class TestClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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.web.server.test.client.reactive.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client.reactive;
|
||||
|
||||
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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.htmlunit;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.htmlunit.StringWebResponse;
|
||||
import org.htmlunit.WebClient;
|
||||
import org.htmlunit.WebConnection;
|
||||
import org.htmlunit.WebResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.assertArg;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalHostWebClient}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LocalHostWebClientTests {
|
||||
|
||||
@Test
|
||||
void createWhenEnvironmentIsNullWillThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new LocalHostWebClient(null))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPageWhenUrlIsRelativeAndNoPortWillUseLocalhost8080() throws Exception {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
WebClient client = new LocalHostWebClient(environment);
|
||||
WebConnection connection = mockConnection();
|
||||
client.setWebConnection(connection);
|
||||
client.getPage("/test");
|
||||
URL expectedUrl = new URL("http://localhost:8080/test");
|
||||
then(connection).should()
|
||||
.getResponse(assertArg((request) -> assertThat(request.getUrl()).isEqualTo(expectedUrl)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPageWhenUrlIsRelativeAndHasPortWillUseLocalhostPort() throws Exception {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("local.server.port", "8181");
|
||||
WebClient client = new LocalHostWebClient(environment);
|
||||
WebConnection connection = mockConnection();
|
||||
client.setWebConnection(connection);
|
||||
client.getPage("/test");
|
||||
URL expectedUrl = new URL("http://localhost:8181/test");
|
||||
then(connection).should()
|
||||
.getResponse(assertArg((request) -> assertThat(request.getUrl()).isEqualTo(expectedUrl)));
|
||||
}
|
||||
|
||||
private WebConnection mockConnection() throws IOException {
|
||||
WebConnection connection = mock(WebConnection.class);
|
||||
WebResponse response = new StringWebResponse("test", new URL("http://localhost"));
|
||||
given(connection.getResponse(any())).willReturn(response);
|
||||
return connection;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.htmlunit.webdriver;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.htmlunit.BrowserVersion;
|
||||
import org.htmlunit.TopLevelWindow;
|
||||
import org.htmlunit.WebClient;
|
||||
import org.htmlunit.WebClientOptions;
|
||||
import org.htmlunit.WebConsole;
|
||||
import org.htmlunit.WebRequest;
|
||||
import org.htmlunit.WebWindow;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.openqa.selenium.Capabilities;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.then;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link LocalHostWebConnectionHtmlUnitDriver}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LocalHostWebConnectionHtmlUnitDriverTests {
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
LocalHostWebConnectionHtmlUnitDriverTests(@Mock WebClient webClient) {
|
||||
this.webClient = webClient;
|
||||
given(this.webClient.getOptions()).willReturn(new WebClientOptions());
|
||||
given(this.webClient.getWebConsole()).willReturn(new WebConsole());
|
||||
WebWindow currentWindow = mock(WebWindow.class);
|
||||
given(currentWindow.isClosed()).willReturn(false);
|
||||
given(this.webClient.getCurrentWindow()).willReturn(currentWindow);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenEnvironmentIsNullWillThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new LocalHostWebConnectionHtmlUnitDriver(null))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithJavascriptFlagWhenEnvironmentIsNullWillThrowException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new LocalHostWebConnectionHtmlUnitDriver(null, true))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithBrowserVersionWhenEnvironmentIsNullWillThrowException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new LocalHostWebConnectionHtmlUnitDriver(null, BrowserVersion.CHROME))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithCapabilitiesWhenEnvironmentIsNullWillThrowException() {
|
||||
Capabilities capabilities = mock(Capabilities.class);
|
||||
given(capabilities.getBrowserName()).willReturn("htmlunit");
|
||||
given(capabilities.getBrowserVersion()).willReturn("chrome");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new LocalHostWebConnectionHtmlUnitDriver(null, capabilities))
|
||||
.withMessageContaining("'environment' must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenUrlIsRelativeAndNoPortWillUseLocalhost8080() throws Exception {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
LocalHostWebConnectionHtmlUnitDriver driver = new TestLocalHostWebConnectionHtmlUnitDriver(environment);
|
||||
driver.get("/test");
|
||||
then(this.webClient).should()
|
||||
.getPage(any(TopLevelWindow.class), requestToUrl(new URL("http://localhost:8080/test")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getWhenUrlIsRelativeAndHasPortWillUseLocalhostPort() throws Exception {
|
||||
MockEnvironment environment = new MockEnvironment();
|
||||
environment.setProperty("local.server.port", "8181");
|
||||
LocalHostWebConnectionHtmlUnitDriver driver = new TestLocalHostWebConnectionHtmlUnitDriver(environment);
|
||||
driver.get("/test");
|
||||
then(this.webClient).should()
|
||||
.getPage(any(TopLevelWindow.class), requestToUrl(new URL("http://localhost:8181/test")));
|
||||
}
|
||||
|
||||
private WebRequest requestToUrl(URL url) {
|
||||
return argThat(new WebRequestUrlArgumentMatcher(url));
|
||||
}
|
||||
|
||||
public class TestLocalHostWebConnectionHtmlUnitDriver extends LocalHostWebConnectionHtmlUnitDriver {
|
||||
|
||||
TestLocalHostWebConnectionHtmlUnitDriver(Environment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebClient getWebClient() {
|
||||
return LocalHostWebConnectionHtmlUnitDriverTests.this.webClient;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class WebRequestUrlArgumentMatcher implements ArgumentMatcher<WebRequest> {
|
||||
|
||||
private final URL expectedUrl;
|
||||
|
||||
private WebRequestUrlArgumentMatcher(URL expectedUrl) {
|
||||
this.expectedUrl = expectedUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(WebRequest argument) {
|
||||
return argument.getUrl().equals(this.expectedUrl);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.reactor.netty;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ReactorResourceFactory;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
class DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactoryTests {
|
||||
|
||||
@Autowired
|
||||
private ReactorResourceFactory reactorResourceFactory;
|
||||
|
||||
@Test
|
||||
void disablesUseGlobalResources() {
|
||||
assertThat(this.reactorResourceFactory.isUseGlobalResources()).isFalse();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
ReactorResourceFactory reactorResourceFactory() {
|
||||
return new ReactorResourceFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright 2012-2025 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.web.server.test.client
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.core.ParameterizedTypeReference
|
||||
import org.springframework.http.HttpEntity
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.RequestEntity
|
||||
import org.springframework.util.ReflectionUtils
|
||||
import org.springframework.web.client.RestOperations
|
||||
import java.net.URI
|
||||
import kotlin.reflect.full.createType
|
||||
import kotlin.reflect.jvm.kotlinFunction
|
||||
|
||||
/**
|
||||
* Mock object based tests for [TestRestTemplate] Kotlin extensions
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class TestRestTemplateExtensionsTests {
|
||||
|
||||
val template = mockk<TestRestTemplate>(relaxed = true)
|
||||
|
||||
@Test
|
||||
fun `getForObject with reified type parameters, String and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.getForObject<Foo>(url, var1, var2)
|
||||
template.restTemplate
|
||||
verify(exactly = 1) { template.getForObject(url, Foo::class.java, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getForObject with reified type parameters, String and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.getForObject<Foo>(url, vars)
|
||||
verify(exactly = 1) { template.getForObject(url, Foo::class.java, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getForObject with reified type parameters and URI`() {
|
||||
val url = URI("https://spring.io")
|
||||
template.getForObject<Foo>(url)
|
||||
verify(exactly = 1) { template.getForObject(url, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getForEntity with reified type parameters and URI`() {
|
||||
val url = URI("https://spring.io")
|
||||
template.getForEntity<Foo>(url)
|
||||
verify(exactly = 1) { template.getForEntity(url, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getForEntity with reified type parameters, String and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.getForEntity<Foo>(url, var1, var2)
|
||||
verify(exactly = 1) { template.getForEntity(url, Foo::class.java, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getForEntity with reified type parameters, String and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.getForEntity<Foo>(url, vars)
|
||||
verify(exactly = 1) { template.getForEntity(url, Foo::class.java, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `patchForObject with reified type parameters, String, Any and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.patchForObject<Foo>(url, body, var1, var2)
|
||||
verify(exactly = 1) { template.patchForObject(url, body, Foo::class.java, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `patchForObject with reified type parameters, String, Any and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.patchForObject<Foo>(url, body, vars)
|
||||
verify(exactly = 1) { template.patchForObject(url, body, Foo::class.java, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `patchForObject with reified type parameters, String and Any`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
template.patchForObject<Foo>(url, body)
|
||||
verify(exactly = 1) { template.patchForObject(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `patchForObject with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.patchForObject<Foo>(url)
|
||||
verify(exactly = 1) { template.patchForObject(url, null, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForObject with reified type parameters, String, Any and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.postForObject<Foo>(url, body, var1, var2)
|
||||
verify(exactly = 1) { template.postForObject(url, body, Foo::class.java, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForObject with reified type parameters, String, Any and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.postForObject<Foo>(url, body, vars)
|
||||
verify(exactly = 1) { template.postForObject(url, body, Foo::class.java, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForObject with reified type parameters, String and Any`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
template.postForObject<Foo>(url, body)
|
||||
verify(exactly = 1) { template.postForObject(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForObject with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.postForObject<Foo>(url)
|
||||
verify(exactly = 1) { template.postForObject(url, null, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForEntity with reified type parameters, String, Any and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.postForEntity<Foo>(url, body, var1, var2)
|
||||
verify(exactly = 1) { template.postForEntity(url, body, Foo::class.java, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForEntity with reified type parameters, String, Any and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.postForEntity<Foo>(url, body, vars)
|
||||
verify(exactly = 1) { template.postForEntity(url, body, Foo::class.java, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForEntity with reified type parameters, String and Any`() {
|
||||
val url = "https://spring.io"
|
||||
val body: Any = "body"
|
||||
template.postForEntity<Foo>(url, body)
|
||||
verify(exactly = 1) { template.postForEntity(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForEntity with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.postForEntity<Foo>(url)
|
||||
verify(exactly = 1) { template.postForEntity(url, null, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters, String, HttpMethod, HttpEntity and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val method = HttpMethod.GET
|
||||
val entity = mockk<HttpEntity<Foo>>()
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.exchange<List<Foo>>(url, method, entity, var1, var2)
|
||||
verify(exactly = 1) { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}, var1, var2) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters, String, HttpMethod, HttpEntity and Map`() {
|
||||
val url = "https://spring.io"
|
||||
val method = HttpMethod.GET
|
||||
val entity = mockk<HttpEntity<Foo>>()
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.exchange<List<Foo>>(url, method, entity, vars)
|
||||
verify(exactly = 1) { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}, vars) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters, String, HttpMethod, HttpEntity`() {
|
||||
val url = "https://spring.io"
|
||||
val method = HttpMethod.GET
|
||||
val entity = mockk<HttpEntity<Foo>>()
|
||||
template.exchange<List<Foo>>(url, method, entity)
|
||||
verify(exactly = 1) { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters and HttpEntity`() {
|
||||
val entity = mockk<RequestEntity<Foo>>()
|
||||
template.exchange<List<Foo>>(entity)
|
||||
verify(exactly = 1) { template.exchange(entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters, String and HttpMethod`() {
|
||||
val url = "https://spring.io"
|
||||
val method = HttpMethod.GET
|
||||
template.exchange<List<Foo>>(url, method)
|
||||
verify(exactly = 1) { template.exchange(url, method, null,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `RestOperations are available`() {
|
||||
val extensions = Class.forName(
|
||||
"org.springframework.boot.web.server.test.client.TestRestTemplateExtensionsKt")
|
||||
ReflectionUtils.doWithMethods(RestOperations::class.java) { method ->
|
||||
arrayOf(ParameterizedTypeReference::class, Class::class).forEach { kClass ->
|
||||
if (method.parameterTypes.contains(kClass.java)) {
|
||||
val parameters = mutableListOf<Class<*>>(TestRestTemplate::class.java)
|
||||
.apply { addAll(method.parameterTypes.filter { it != kClass.java }) }
|
||||
val f = extensions.getDeclaredMethod(method.name,
|
||||
*parameters.toTypedArray()).kotlinFunction!!
|
||||
assertThat(f.typeParameters.size).isEqualTo(1)
|
||||
assertThat(listOf(Any::class.createType()))
|
||||
.isEqualTo(f.typeParameters[0].upperBounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Foo
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user