Add support for RestTestClient
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Trigger Docs Build (push) Blocked by required conditions
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:24], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:24], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:17], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:25], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:25], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
Run CodeQL Analysis / run-analysis (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:17]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Waiting to run
Details
Build and Deploy Snapshot / Build and Deploy Snapshot (push) Waiting to run
Details
Build and Deploy Snapshot / Trigger Docs Build (push) Blocked by required conditions
Details
Build and Deploy Snapshot / Verify (push) Blocked by required conditions
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:24], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:false version:24], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:17], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:17], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:21], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:25], map[id:${{ vars.UBUNTU_MEDIUM || 'ubuntu-latest' }} name:Linux]) (push) Waiting to run
Details
CI / ${{ matrix.os.name}} | Java ${{ matrix.java.version}} (map[toolchain:true version:25], map[id:windows-latest name:Windows]) (push) Waiting to run
Details
Run CodeQL Analysis / run-analysis (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:17]) (push) Waiting to run
Details
Run System Tests / Java ${{ matrix.java.version}} (map[toolchain:true version:21]) (push) Waiting to run
Details
This commit adds support for RestTestClient for MockMvc and integration tests. Closes gh-47335
This commit is contained in:
parent
e2ba4dad2a
commit
787840735b
|
@ -141,10 +141,11 @@ include-code::MyApplicationArgumentTests[]
|
||||||
By default, javadoc:org.springframework.boot.test.context.SpringBootTest[format=annotation] does not start the server but instead sets up a mock environment for testing web endpoints.
|
By default, javadoc:org.springframework.boot.test.context.SpringBootTest[format=annotation] does not start the server but instead sets up a mock environment for testing web endpoints.
|
||||||
|
|
||||||
With Spring MVC, we can query our web endpoints using {url-spring-framework-docs}/testing/mockmvc.html[`MockMvc`].
|
With Spring MVC, we can query our web endpoints using {url-spring-framework-docs}/testing/mockmvc.html[`MockMvc`].
|
||||||
Three integrations are available:
|
The following integrations are available:
|
||||||
|
|
||||||
* The regular {url-spring-framework-docs}/testing/mockmvc/hamcrest.html[`MockMvc`] that uses Hamcrest.
|
* The regular {url-spring-framework-docs}/testing/mockmvc/hamcrest.html[`MockMvc`] that uses Hamcrest.
|
||||||
* {url-spring-framework-docs}/testing/mockmvc/assertj.html[`MockMvcTester`] that wraps javadoc:org.springframework.test.web.servlet.MockMvc[] and uses AssertJ.
|
* {url-spring-framework-docs}/testing/mockmvc/assertj.html[`MockMvcTester`] that wraps javadoc:org.springframework.test.web.servlet.MockMvc[] and uses AssertJ.
|
||||||
|
* {url-spring-framework-docs}/testing/resttestclient.html[`RestTestClient`] where javadoc:org.springframework.test.web.servlet.MockMvc[] is plugged in as the server to handle requests with.
|
||||||
* {url-spring-framework-docs}/testing/webtestclient.html[`WebTestClient`] where javadoc:org.springframework.test.web.servlet.MockMvc[] is plugged in as the server to handle requests with.
|
* {url-spring-framework-docs}/testing/webtestclient.html[`WebTestClient`] where javadoc:org.springframework.test.web.servlet.MockMvc[] is plugged in as the server to handle requests with.
|
||||||
|
|
||||||
The following example showcases the available integrations:
|
The following example showcases the available integrations:
|
||||||
|
@ -176,19 +177,32 @@ If you need to start a full running server, we recommend that you use random por
|
||||||
If you use `@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`, an available port is picked at random each time your test runs.
|
If you use `@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)`, an available port is picked at random each time your test runs.
|
||||||
|
|
||||||
The javadoc:org.springframework.boot.test.web.server.LocalServerPort[format=annotation] annotation can be used to xref:how-to:webserver.adoc#howto.webserver.discover-port[inject the actual port used] into your test.
|
The javadoc:org.springframework.boot.test.web.server.LocalServerPort[format=annotation] annotation can be used to xref:how-to:webserver.adoc#howto.webserver.discover-port[inject the actual port used] into your test.
|
||||||
For convenience, tests that need to make REST calls to the started server can additionally autowire a {url-spring-framework-docs}/testing/webtestclient.html[`WebTestClient`], which resolves relative links to the running server and comes with a dedicated API for verifying responses, as shown in the following example:
|
|
||||||
|
For convenience, tests that need to make REST calls to the started server can additionally autowire a
|
||||||
|
{url-spring-framework-docs}/testing/resttestclient.html[`RestTestClient`] which resolves relative links to the running server and comes with a dedicated API for verifying responses, as shown in the following example:
|
||||||
|
|
||||||
|
include-code::MyRandomPortRestTestClientTests[]
|
||||||
|
|
||||||
|
If you have `spring-webflux` on the classpath, you can also autowire a {url-spring-framework-docs}/testing/webtestclient.html[`WebTestClient`] that provides a similar API:
|
||||||
|
|
||||||
include-code::MyRandomPortWebTestClientTests[]
|
include-code::MyRandomPortWebTestClientTests[]
|
||||||
|
|
||||||
TIP: javadoc:org.springframework.test.web.reactive.server.WebTestClient[] can also used with a xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-mock-environment[mock environment], removing the need for a running server, by annotating your test class with javadoc:org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient[format=annotation] from `spring-boot-webflux-test`.
|
TIP: javadoc:org.springframework.test.web.reactive.server.WebTestClient[] can also used with a xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-mock-environment[mock environment], removing the need for a running server, by annotating your test class with javadoc:org.springframework.boot.webflux.test.autoconfigure.AutoConfigureWebTestClient[format=annotation] from `spring-boot-webflux-test`.
|
||||||
|
|
||||||
This setup requires `spring-webflux` on the classpath.
|
The `spring-boot-web-server-test` modules also provides a javadoc:org.springframework.boot.web.server.test.client.TestRestTemplate[] facility:
|
||||||
If you can not or will not add webflux, the `spring-boot-web-server-test` modules provides a javadoc:org.springframework.boot.web.server.test.client.TestRestTemplate[] facility:
|
|
||||||
|
|
||||||
include-code::MyRandomPortTestRestTemplateTests[]
|
include-code::MyRandomPortTestRestTemplateTests[]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[testing.spring-boot-applications.customizing-rest-test-client]]
|
||||||
|
== Customizing RestTestClient
|
||||||
|
|
||||||
|
To customize the javadoc:org.springframework.test.web.servlet.client.RestTestClient[] bean, configure a javadoc:org.springframework.boot.web.server.test.client.RestTestClientBuilderCustomizer[] bean.
|
||||||
|
Any such beans are called with the javadoc:org.springframework.test.web.servlet.client.RestTestClient$Builder[] that is used to create the javadoc:org.springframework.test.web.servlet.client.RestTestClient[].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[testing.spring-boot-applications.customizing-web-test-client]]
|
[[testing.spring-boot-applications.customizing-web-test-client]]
|
||||||
== Customizing WebTestClient
|
== Customizing WebTestClient
|
||||||
|
|
||||||
|
|
|
@ -48,8 +48,9 @@ In either case, the template is fault tolerant.
|
||||||
This means that it behaves in a test-friendly way by not throwing exceptions on 4xx and 5xx errors.
|
This means that it behaves in a test-friendly way by not throwing exceptions on 4xx and 5xx errors.
|
||||||
Instead, such errors can be detected through the returned javadoc:org.springframework.http.ResponseEntity[] and its status code.
|
Instead, such errors can be detected through the returned javadoc:org.springframework.http.ResponseEntity[] and its status code.
|
||||||
|
|
||||||
TIP: Spring Framework 5.0 provides a new javadoc:org.springframework.test.web.reactive.server.WebTestClient[] that works for xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.spring-webflux-tests[WebFlux integration tests] and both xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-running-server[WebFlux and MVC end-to-end testing].
|
If you need fluent API for assertions, consider using javadoc:org.springframework.test.web.servlet.client.RestTestClient[] that works with xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-mock-environment[mock environments] and xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-running-server[end-to-end tests].
|
||||||
It provides a fluent API for assertions, unlike javadoc:org.springframework.boot.test.web.client.TestRestTemplate[].
|
|
||||||
|
If you are using Spring WebFlux, consider the javadoc:org.springframework.test.web.reactive.server.WebTestClient[] that provides a similar API and works with xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-mock-environment[mock environments], xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.spring-webflux-tests[WebFlux integration tests], and xref:testing/spring-boot-applications.adoc#testing.spring-boot-applications.with-running-server[end-to-end tests].
|
||||||
|
|
||||||
It is recommended, but not mandatory, to use the Apache HTTP Client (version 5.1 or better).
|
It is recommended, but not mandatory, to use the Apache HTTP Client (version 5.1 or better).
|
||||||
If you have that on your classpath, the javadoc:org.springframework.boot.test.web.client.TestRestTemplate[] responds by configuring the client appropriately.
|
If you have that on your classpath, the javadoc:org.springframework.boot.test.web.client.TestRestTemplate[] responds by configuring the client appropriately.
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
@ -45,6 +46,17 @@ class MyMockMvcTests {
|
||||||
assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("Hello World");
|
assertThat(mvc.get().uri("/")).hasStatusOk().hasBodyTextEqualTo("Hello World");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testWithRestTestClient(@Autowired RestTestClient webClient) {
|
||||||
|
// @formatter:off
|
||||||
|
webClient
|
||||||
|
.get().uri("/")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBody(String.class).isEqualTo("Hello World");
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
|
// If Spring WebFlux is on the classpath, you can drive MVC tests with a WebTestClient
|
||||||
@Test
|
@Test
|
||||||
void testWithWebTestClient(@Autowired WebTestClient webClient) {
|
void testWithWebTestClient(@Autowired WebTestClient webClient) {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.docs.testing.springbootapplications.withrunningserver;
|
||||||
|
|
||||||
|
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.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
class MyRandomPortRestTestClientTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exampleTest(@Autowired RestTestClient webClient) {
|
||||||
|
// @formatter:off
|
||||||
|
webClient
|
||||||
|
.get().uri("/")
|
||||||
|
.exchange()
|
||||||
|
.expectStatus().isOk()
|
||||||
|
.expectBody(String.class).isEqualTo("Hello World");
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,7 +31,12 @@ import org.springframework.web.util.UriTemplateHandler;
|
||||||
*/
|
*/
|
||||||
public class RootUriBuilderFactory extends RootUriTemplateHandler implements UriBuilderFactory {
|
public class RootUriBuilderFactory extends RootUriTemplateHandler implements UriBuilderFactory {
|
||||||
|
|
||||||
RootUriBuilderFactory(String rootUri, UriTemplateHandler delegate) {
|
/**
|
||||||
|
* Create an instance with the root URI to use.
|
||||||
|
* @param rootUri the root URI
|
||||||
|
* @param delegate the {@link UriTemplateHandler} to delegate to
|
||||||
|
*/
|
||||||
|
public RootUriBuilderFactory(String rootUri, UriTemplateHandler delegate) {
|
||||||
super(rootUri, delegate);
|
super(rootUri, delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.server.test.client;
|
||||||
|
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A customizer that can be implemented by beans wishing to customize the
|
||||||
|
* {@link RestTestClient.Builder} to fine-tine its auto-configuration before a
|
||||||
|
* {@link RestTestClient} is created.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RestTestClientBuilderCustomizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the given {@link RestTestClient.Builder Builder}.
|
||||||
|
* @param builder the builder
|
||||||
|
*/
|
||||||
|
void customize(RestTestClient.Builder<?> builder);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.server.test.client;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.aot.AotDetector;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
|
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||||
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||||
|
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.boot.restclient.RootUriBuilderFactory;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
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.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContextCustomizer} for {@link RestTestClient}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class RestTestClientContextCustomizer implements ContextCustomizer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
|
||||||
|
if (AotDetector.useGeneratedArtifacts()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(mergedConfig.getTestClass(),
|
||||||
|
SpringBootTest.class);
|
||||||
|
Assert.state(springBootTest != null, "'springBootTest' must not be null");
|
||||||
|
if (springBootTest.webEnvironment().isEmbedded()) {
|
||||||
|
registerRestTestClient(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerRestTestClient(ConfigurableApplicationContext context) {
|
||||||
|
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||||
|
if (beanFactory instanceof BeanDefinitionRegistry registry) {
|
||||||
|
registerRestTestClient(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerRestTestClient(BeanDefinitionRegistry registry) {
|
||||||
|
RootBeanDefinition definition = new RootBeanDefinition(RestTestClientRegistrar.class);
|
||||||
|
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||||
|
registry.registerBeanDefinition(RestTestClientRegistrar.class.getName(), definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object obj) {
|
||||||
|
return (obj != null) && (obj.getClass() == getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getClass().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link BeanDefinitionRegistryPostProcessor} that runs after the
|
||||||
|
* {@link ConfigurationClassPostProcessor} and add a {@link RestTestClientFactory}
|
||||||
|
* bean definition when a {@link RestTestClient} hasn't already been registered.
|
||||||
|
*/
|
||||||
|
static class RestTestClientRegistrar implements BeanDefinitionRegistryPostProcessor, Ordered, BeanFactoryAware {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
|
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,
|
||||||
|
RestTestClient.class, false, false).length == 0) {
|
||||||
|
registry.registerBeanDefinition(RestTestClient.class.getName(),
|
||||||
|
new RootBeanDefinition(RestTestClientFactory.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link FactoryBean} used to create and configure a {@link RestTestClient}.
|
||||||
|
*/
|
||||||
|
public static class RestTestClientFactory implements FactoryBean<RestTestClient>, ApplicationContextAware {
|
||||||
|
|
||||||
|
@SuppressWarnings("NullAway.Init")
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
private @Nullable RestTestClient object;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSingleton() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getObjectType() {
|
||||||
|
return RestTestClient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestTestClient getObject() {
|
||||||
|
if (this.object == null) {
|
||||||
|
this.object = createRestTestClient();
|
||||||
|
}
|
||||||
|
return this.object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RestTestClient createRestTestClient() {
|
||||||
|
boolean sslEnabled = isSslEnabled(this.applicationContext);
|
||||||
|
LocalHostUriTemplateHandler handler = new LocalHostUriTemplateHandler(
|
||||||
|
this.applicationContext.getEnvironment(), sslEnabled ? "https" : "http");
|
||||||
|
RestTestClient.Builder<?> builder = RestTestClient.bindToServer();
|
||||||
|
customizeRestTestClientBuilder(builder, this.applicationContext);
|
||||||
|
return builder.uriBuilderFactory(new RootUriBuilderFactory(handler.getRootUri(), handler)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 customizeRestTestClientBuilder(RestTestClient.Builder<?> clientBuilder,
|
||||||
|
ApplicationContext context) {
|
||||||
|
for (RestTestClientBuilderCustomizer customizer : context
|
||||||
|
.getBeansOfType(RestTestClientBuilderCustomizer.class)
|
||||||
|
.values()) {
|
||||||
|
customizer.customize(clientBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.server.test.client;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||||
|
import org.springframework.test.context.ContextCustomizer;
|
||||||
|
import org.springframework.test.context.ContextCustomizerFactory;
|
||||||
|
import org.springframework.test.context.TestContextAnnotationUtils;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContextCustomizerFactory} for {@code RestTestClient}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
class RestTestClientContextCustomizerFactory implements ContextCustomizerFactory {
|
||||||
|
|
||||||
|
private static final boolean restClientPresent;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ClassLoader loader = RestTestClientContextCustomizerFactory.class.getClassLoader();
|
||||||
|
restClientPresent = ClassUtils.isPresent("org.springframework.web.client.RestClient", loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||||
|
List<ContextConfigurationAttributes> configAttributes) {
|
||||||
|
SpringBootTest springBootTest = TestContextAnnotationUtils.findMergedAnnotation(testClass,
|
||||||
|
SpringBootTest.class);
|
||||||
|
return (springBootTest != null && restClientPresent) ? new RestTestClientContextCustomizer() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ org.springframework.boot.web.server.test.SpringBootTestRandomPortEnvironmentPost
|
||||||
|
|
||||||
# Spring Test Context Customizer Factories
|
# Spring Test Context Customizer Factories
|
||||||
org.springframework.test.context.ContextCustomizerFactory=\
|
org.springframework.test.context.ContextCustomizerFactory=\
|
||||||
|
org.springframework.boot.web.server.test.client.RestTestClientContextCustomizerFactory,\
|
||||||
org.springframework.boot.web.server.test.client.TestRestTemplateContextCustomizerFactory,\
|
org.springframework.boot.web.server.test.client.TestRestTemplateContextCustomizerFactory,\
|
||||||
org.springframework.boot.web.server.test.client.reactive.WebTestClientContextCustomizerFactory,\
|
org.springframework.boot.web.server.test.client.reactive.WebTestClientContextCustomizerFactory,\
|
||||||
org.springframework.boot.web.server.test.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory
|
org.springframework.boot.web.server.test.reactor.netty.DisableReactorResourceFactoryGlobalResourcesContextCustomizerFactory
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.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 org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ImportSelector} to check no {@link RestTestClient} definition is registered when
|
||||||
|
* config classes are processed.
|
||||||
|
*/
|
||||||
|
class NoRestTestClientBeanChecker implements ImportSelector, BeanFactoryAware {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBeanFactory(BeanFactory beanFactory) {
|
||||||
|
assertThat(BeanFactoryUtils.beanNamesForTypeIncludingAncestors((ListableBeanFactory) beanFactory,
|
||||||
|
RestTestClient.class))
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.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.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.then;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for {@link RestTestClientContextCustomizer}.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@DirtiesContext
|
||||||
|
class RestTestClientContextCustomizerIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTestClient webClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTestClientBuilderCustomizer clientBuilderCustomizer;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
then(this.clientBuilderCustomizer).should().customize(any(RestTestClient.Builder.class));
|
||||||
|
this.webClient.get().uri("/").exchange().expectBody(String.class).isEqualTo("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import({ TestWebMvcConfiguration.class, NoRestTestClientBeanChecker.class })
|
||||||
|
@RestController
|
||||||
|
static class TestConfig {
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
String root() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RestTestClientBuilderCustomizer clientBuilderCustomizer() {
|
||||||
|
return mock(RestTestClientBuilderCustomizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.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.RestTestClientContextCustomizer.RestTestClientRegistrar;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.support.AbstractApplicationContext;
|
||||||
|
import org.springframework.test.context.MergedContextConfiguration;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RestTestClientContextCustomizer}.
|
||||||
|
*
|
||||||
|
* @author Moritz Halbritter
|
||||||
|
*/
|
||||||
|
class RestTestClientContextCustomizerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenContextIsNotABeanDefinitionRegistryRestTestClientIsRegistered() {
|
||||||
|
new ApplicationContextRunner(TestApplicationContext::new)
|
||||||
|
.withInitializer(this::applyRestTestClientContextCustomizer)
|
||||||
|
.run((context) -> assertThat(context).hasSingleBean(RestTestClient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void whenUsingAotGeneratedArtifactsRestTestClientIsNotRegistered() {
|
||||||
|
new ApplicationContextRunner().withSystemProperties("spring.aot.enabled:true")
|
||||||
|
.withInitializer(this::applyRestTestClientContextCustomizer)
|
||||||
|
.run((context) -> {
|
||||||
|
assertThat(context).doesNotHaveBean(RestTestClientRegistrar.class);
|
||||||
|
assertThat(context).doesNotHaveBean(RestTestClient.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
void applyRestTestClientContextCustomizer(ConfigurableApplicationContext context) {
|
||||||
|
MergedContextConfiguration configuration = mock(MergedContextConfiguration.class);
|
||||||
|
given(configuration.getTestClass()).willReturn((Class) TestClass.class);
|
||||||
|
new RestTestClientContextCustomizer().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,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.server.test.client;
|
||||||
|
|
||||||
|
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.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for {@link RestTestClientContextCustomizer} with a custom context
|
||||||
|
* path.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
@TestPropertySource(properties = "server.servlet.context-path=/test")
|
||||||
|
class RestTestClientContextCustomizerWithCustomContextPathIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTestClient webClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
this.webClient.get().uri("/").exchange().expectBody(String.class).isEqualTo("hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(NoRestTestClientBeanChecker.class)
|
||||||
|
@RestController
|
||||||
|
static class TestConfig {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
factory.setContextPath("/test");
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static PropertySourcesPlaceholderConfigurer propertyPlaceholder() {
|
||||||
|
return new PropertySourcesPlaceholderConfigurer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
String root() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.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.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for {@link RestTestClientContextCustomizer} with a custom client.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
|
||||||
|
class RestTestClientContextCustomizerWithOverridePathIntegrationTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RestTestClient webClient;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test() {
|
||||||
|
assertThat(this.webClient).isInstanceOf(CustomRestTestClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import({ TestWebMvcConfiguration.class, NoRestTestClientBeanChecker.class })
|
||||||
|
@RestController
|
||||||
|
static class TestConfig {
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
String root() {
|
||||||
|
return "hello";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
CustomRestTestClient customRestTestClient() {
|
||||||
|
return mock(CustomRestTestClient.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CustomRestTestClient extends RestTestClient {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2012-present the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.boot.web.server.test.client;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory;
|
||||||
|
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
|
||||||
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableWebMvc
|
||||||
|
class TestWebMvcConfiguration {
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicat
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
|
||||||
|
import org.springframework.boot.web.server.test.client.RestTestClientBuilderCustomizer;
|
||||||
import org.springframework.boot.web.server.test.client.reactive.WebTestClientBuilderCustomizer;
|
import org.springframework.boot.web.server.test.client.reactive.WebTestClientBuilderCustomizer;
|
||||||
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath;
|
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletPath;
|
||||||
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
|
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
|
||||||
|
@ -35,6 +36,8 @@ import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
|
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
@ -82,4 +85,20 @@ public final class MockMvcAutoConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnClass({ RestClient.class, RestTestClient.class })
|
||||||
|
static class RestTestClientMockMvcConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
RestTestClient restTestClient(MockMvc mockMvc, List<RestTestClientBuilderCustomizer> customizers) {
|
||||||
|
RestTestClient.Builder<?> builder = RestTestClient.bindTo(mockMvc);
|
||||||
|
for (RestTestClientBuilderCustomizer customizer : customizers) {
|
||||||
|
customizer.customize(builder);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||||
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
|
||||||
|
import org.springframework.boot.web.server.test.client.RestTestClientBuilderCustomizer;
|
||||||
import org.springframework.boot.web.server.test.client.reactive.WebTestClientBuilderCustomizer;
|
import org.springframework.boot.web.server.test.client.reactive.WebTestClientBuilderCustomizer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -28,6 +29,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
import org.springframework.test.web.servlet.RequestBuilder;
|
import org.springframework.test.web.servlet.RequestBuilder;
|
||||||
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
import org.springframework.web.client.RestClient;
|
||||||
import org.springframework.web.reactive.function.client.WebClient;
|
import org.springframework.web.reactive.function.client.WebClient;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
|
@ -41,6 +44,7 @@ import static org.mockito.Mockito.mock;
|
||||||
*
|
*
|
||||||
* @author Madhura Bhave
|
* @author Madhura Bhave
|
||||||
* @author Brian Clozel
|
* @author Brian Clozel
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class MockMvcAutoConfigurationTests {
|
class MockMvcAutoConfigurationTests {
|
||||||
|
|
||||||
|
@ -99,6 +103,27 @@ class MockMvcAutoConfigurationTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registersRestTestClient() {
|
||||||
|
this.contextRunner.run((context) -> assertThat(context).hasSingleBean(RestTestClient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRegisterRestTestClientIfRestClientIsMissing() {
|
||||||
|
this.contextRunner.withClassLoader(new FilteredClassLoader(RestClient.class))
|
||||||
|
.run((context) -> assertThat(context).doesNotHaveBean(RestTestClient.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldApplyRestTestClientCustomizers() {
|
||||||
|
this.contextRunner.withUserConfiguration(RestTestClientCustomConfig.class).run((context) -> {
|
||||||
|
assertThat(context).hasSingleBean(RestTestClient.class);
|
||||||
|
assertThat(context).hasBean("myRestTestClientCustomizer");
|
||||||
|
then(context.getBean("myRestTestClientCustomizer", RestTestClientBuilderCustomizer.class)).should()
|
||||||
|
.customize(any(RestTestClient.Builder.class));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class WebTestClientCustomConfig {
|
static class WebTestClientCustomConfig {
|
||||||
|
|
||||||
|
@ -109,4 +134,14 @@ class MockMvcAutoConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
static class RestTestClientCustomConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RestTestClientBuilderCustomizer myRestTestClientCustomizer() {
|
||||||
|
return mock(RestTestClientBuilderCustomizer.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
@ -84,6 +85,11 @@ class MockMvcSpringBootTestIntegrationTests {
|
||||||
webTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
webTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestWithRestTestClient(@Autowired RestTestClient restTestClient) {
|
||||||
|
restTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotFailIfFormattingValueThrowsException(CapturedOutput output) throws Exception {
|
void shouldNotFailIfFormattingValueThrowsException(CapturedOutput output) throws Exception {
|
||||||
this.mvc.perform(get("/formatting")).andExpect(content().string("formatting")).andExpect(status().isOk());
|
this.mvc.perform(get("/formatting")).andExpect(content().string("formatting")).andExpect(status().isOk());
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||||
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
||||||
|
import org.springframework.test.web.servlet.client.RestTestClient;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@ -80,6 +81,11 @@ class MockMvcTesterSpringBootTestIntegrationTests {
|
||||||
webTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
webTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestWithRestTestClient(@Autowired RestTestClient restTestClient) {
|
||||||
|
restTestClient.get().uri("/one").exchange().expectStatus().isOk().expectBody(String.class).isEqualTo("one");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldNotFailIfFormattingValueThrowsException(CapturedOutput output) {
|
void shouldNotFailIfFormattingValueThrowsException(CapturedOutput output) {
|
||||||
assertThat(this.mvc.get().uri("/formatting")).hasStatusOk().hasBodyTextEqualTo("formatting");
|
assertThat(this.mvc.get().uri("/formatting")).hasStatusOk().hasBodyTextEqualTo("formatting");
|
||||||
|
|
Loading…
Reference in New Issue