diff --git a/framework-docs/src/docs/asciidoc/testing/spring-mvc-test-client.adoc b/framework-docs/src/docs/asciidoc/testing/spring-mvc-test-client.adoc index 83f09bd3e2..1bbd496e26 100644 --- a/framework-docs/src/docs/asciidoc/testing/spring-mvc-test-client.adoc +++ b/framework-docs/src/docs/asciidoc/testing/spring-mvc-test-client.adoc @@ -117,21 +117,21 @@ logic but without running a server. The following example shows how to do so: // Test code that uses the above RestTemplate ... ---- -In the more specific cases where total isolation isn't desired and some integration testing -of one or more calls is needed, a specific `ResponseCreator` can be set up in advance and -used to perform actual requests and assert the response. -The following example shows how to set up and use the `ExecutingResponseCreator` to do so: +In some cases it may be necessary to perform an actual call to a remote service instead +of mocking the response. The following example shows how to do that through +`ExecutingResponseCreator`: [source,java,indent=0,subs="verbatim,quotes",role="primary"] .Java ---- RestTemplate restTemplate = new RestTemplate(); - // Make sure to capture the request factory of the RestTemplate before binding + // Create ExecutingResponseCreator with the original request factory ExecutingResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory()); MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build(); - mockServer.expect(requestTo("/greeting")).andRespond(withActualResponse); + mockServer.expect(requestTo("/profile")).andRespond(withSuccess()); + mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse); // Test code that uses the above RestTemplate ... @@ -142,7 +142,7 @@ The following example shows how to set up and use the `ExecutingResponseCreator` ---- val restTemplate = RestTemplate() - // Make sure to capture the request factory of the RestTemplate before binding + // Create ExecutingResponseCreator with the original request factory val withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory()) val mockServer = MockRestServiceServer.bindTo(restTemplate).build() @@ -156,16 +156,15 @@ The following example shows how to set up and use the `ExecutingResponseCreator` In the preceding example, we create the `ExecutingResponseCreator` using the `ClientHttpRequestFactory` from the `RestTemplate` _before_ `MockRestServiceServer` replaces -it with the custom one. -Then we define expectations with two kinds of response: +it with a different one that mocks responses. +Then we define expectations with two kinds of responses: * a stub `200` response for the `/profile` endpoint (no actual request will be executed) - * an "executing response" for the `/quoteOfTheDay` endpoint + * a response obtained through a call to the `/quoteOfTheDay` endpoint -In the second case, the request is executed by the `ClientHttpRequestFactory` that was +In the second case, the request is executed through the `ClientHttpRequestFactory` that was captured earlier. This generates a response that could e.g. come from an actual remote server, -depending on how the `RestTemplate` was originally configured, and MockMVC can be further -used to assert the content of the response. +depending on how the `RestTemplate` was originally configured. [[spring-mvc-test-client-static-imports]] == Static Imports diff --git a/spring-test/src/main/java/org/springframework/test/web/client/response/ExecutingResponseCreator.java b/spring-test/src/main/java/org/springframework/test/web/client/response/ExecutingResponseCreator.java index deb69a3b63..6035f368f5 100644 --- a/spring-test/src/main/java/org/springframework/test/web/client/response/ExecutingResponseCreator.java +++ b/spring-test/src/main/java/org/springframework/test/web/client/response/ExecutingResponseCreator.java @@ -27,15 +27,12 @@ import org.springframework.util.Assert; import org.springframework.util.StreamUtils; /** - * A {@code ResponseCreator} which delegates to a {@link ClientHttpRequestFactory} - * to perform the request and return the associated response. - * This is notably useful when testing code that calls multiple remote services, some - * of which need to be actually called rather than further mocked. - *
Note that the input request is asserted to be a {@code MockClientHttpRequest} and - * the URI, method, headers and body are copied. - *
The factory can typically be obtained from a {@code RestTemplate} but in case this - * is used with e.g. {@code MockRestServiceServer}, make sure to capture the factory early - * before binding the mock server to the RestTemplate (as it replaces the factory): + * {@code ResponseCreator} that obtains the response by executing the request + * through a {@link ClientHttpRequestFactory}. This is useful in scenarios with + * multiple remote services where some need to be called rather than mocked. + *
The {@code ClientHttpRequestFactory} is typically obtained from the + * {@code RestTemplate} before it is passed to {@code MockRestServiceServer}, + * in effect using the original factory rather than the test factory: *
* ResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate);
* MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
@@ -53,7 +50,7 @@ public class ExecutingResponseCreator implements ResponseCreator {
/**
- * Create a {@code ExecutingResponseCreator} from a {@code ClientHttpRequestFactory}.
+ * Create an instance with the given {@code ClientHttpRequestFactory}.
* @param requestFactory the request factory to delegate to
*/
public ExecutingResponseCreator(ClientHttpRequestFactory requestFactory) {
@@ -63,7 +60,7 @@ public class ExecutingResponseCreator implements ResponseCreator {
@Override
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
- Assert.state(request instanceof MockClientHttpRequest, "Request should be an instance of MockClientHttpRequest");
+ Assert.state(request instanceof MockClientHttpRequest, "Expected a MockClientHttpRequest");
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
ClientHttpRequest newRequest = this.requestFactory.createRequest(mockRequest.getURI(), mockRequest.getMethod());
newRequest.getHeaders().putAll(mockRequest.getHeaders());
diff --git a/spring-test/src/main/java/org/springframework/test/web/client/response/MockRestResponseCreators.java b/spring-test/src/main/java/org/springframework/test/web/client/response/MockRestResponseCreators.java
index 38ea2c1e3e..7502990468 100644
--- a/spring-test/src/main/java/org/springframework/test/web/client/response/MockRestResponseCreators.java
+++ b/spring-test/src/main/java/org/springframework/test/web/client/response/MockRestResponseCreators.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-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.
@@ -28,16 +28,11 @@ import org.springframework.lang.Nullable;
import org.springframework.test.web.client.ResponseCreator;
/**
- * Static factory methods for obtaining a {@link ResponseCreator} instance.
+ * Static factory methods to obtain a {@link ResponseCreator} with a fixed
+ * response.
*
- * Eclipse users: consider adding this class as a Java editor
- * favorite. To navigate, open the Preferences and type "favorites".
- *
- *
See also {@link ExecutingResponseCreator} for a {@code ResponseCreator} that is
- * capable of performing an actual request. That case is not offered as a factory method
- * here because of the early setup that is likely needed (capturing a request factory
- * which wouldn't be available anymore when the factory methods are typically invoked,
- * e.g. replaced in a {@code RestTemplate} by the {@code MockRestServiceServer}).
+ *
In addition, see also the {@link ExecutingResponseCreator} implementation
+ * that performs an actual requests to remote services.
*
* @author Rossen Stoyanchev
* @since 3.2
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java b/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java
index c8f536c9aa..92b95bc3d6 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-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.
@@ -99,39 +99,34 @@ class MockRestServiceServerTests {
@Test
void executingResponseCreator() {
- RestTemplate restTemplateWithMockEcho = createEchoRestTemplate();
+ RestTemplate restTemplate = createEchoRestTemplate();
+ ExecutingResponseCreator withActualCall = new ExecutingResponseCreator(restTemplate.getRequestFactory());
- final ExecutingResponseCreator withActualCall = new ExecutingResponseCreator(restTemplateWithMockEcho.getRequestFactory());
- MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplateWithMockEcho).build();
+ MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
server.expect(requestTo("/profile")).andRespond(withSuccess());
server.expect(requestTo("/quoteOfTheDay")).andRespond(withActualCall);
- var response1 = restTemplateWithMockEcho.getForEntity("/profile", String.class);
- var response2 = restTemplateWithMockEcho.getForEntity("/quoteOfTheDay", String.class);
+ var response1 = restTemplate.getForEntity("/profile", String.class);
+ var response2 = restTemplate.getForEntity("/quoteOfTheDay", String.class);
server.verify();
- assertThat(response1.getStatusCode().value())
- .as("response1 status").isEqualTo(200);
- assertThat(response1.getBody())
- .as("response1 body").isNullOrEmpty();
- assertThat(response2.getStatusCode().value())
- .as("response2 status").isEqualTo(300);
- assertThat(response2.getBody())
- .as("response2 body").isEqualTo("echo from /quoteOfTheDay");
+ assertThat(response1.getStatusCode().value()).isEqualTo(200);
+ assertThat(response1.getBody()).isNullOrEmpty();
+ assertThat(response2.getStatusCode().value()).isEqualTo(300);
+ assertThat(response2.getBody()).isEqualTo("echo from /quoteOfTheDay");
}
private static RestTemplate createEchoRestTemplate() {
- final ClientHttpRequestFactory echoRequestFactory = (uri, httpMethod) -> {
- final MockClientHttpRequest req = new MockClientHttpRequest(httpMethod, uri);
- String body = "echo from " + uri.getPath();
- final ClientHttpResponse resp = new MockClientHttpResponse(body.getBytes(StandardCharsets.UTF_8),
- // Instead of 200, we use a less-common status code on purpose
- HttpStatus.MULTIPLE_CHOICES);
- resp.getHeaders().setContentType(MediaType.TEXT_PLAIN);
- req.setResponse(resp);
- return req;
+ ClientHttpRequestFactory requestFactory = (uri, httpMethod) -> {
+ MockClientHttpRequest request = new MockClientHttpRequest(httpMethod, uri);
+ ClientHttpResponse response = new MockClientHttpResponse(
+ ("echo from " + uri.getPath()).getBytes(StandardCharsets.UTF_8),
+ HttpStatus.MULTIPLE_CHOICES); // use a different status on purpose
+ response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
+ request.setResponse(response);
+ return request;
};
- return new RestTemplate(echoRequestFactory);
+ return new RestTemplate(requestFactory);
}
@Test
diff --git a/spring-test/src/test/java/org/springframework/test/web/client/response/ExecutingResponseCreatorTests.java b/spring-test/src/test/java/org/springframework/test/web/client/response/ExecutingResponseCreatorTests.java
index 4c5efcc897..63982612a3 100644
--- a/spring-test/src/test/java/org/springframework/test/web/client/response/ExecutingResponseCreatorTests.java
+++ b/spring-test/src/test/java/org/springframework/test/web/client/response/ExecutingResponseCreatorTests.java
@@ -17,17 +17,13 @@
package org.springframework.test.web.client.response;
import java.io.IOException;
-import java.io.OutputStream;
-import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
-import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
-import org.springframework.http.client.AbstractClientHttpRequest;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
@@ -36,6 +32,7 @@ import org.springframework.mock.http.client.MockClientHttpResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.Mockito.mock;
/**
* Tests for the {@link ExecutingResponseCreator} implementation.
@@ -46,50 +43,29 @@ class ExecutingResponseCreatorTests {
@Test
void ensureRequestNotNull() {
- final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
+ ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
assertThatIllegalStateException()
.isThrownBy(() -> responseCreator.createResponse(null))
- .withMessage("Request should be an instance of MockClientHttpRequest");
+ .withMessage("Expected a MockClientHttpRequest");
}
@Test
void ensureRequestIsMock() {
final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
- ClientHttpRequest notAMockRequest = new AbstractClientHttpRequest() {
- @Override
- protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
- return null;
- }
-
- @Override
- protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
- return null;
- }
-
- @Override
- public HttpMethod getMethod() {
- return null;
- }
-
- @Override
- public URI getURI() {
- return null;
- }
- };
+ ClientHttpRequest notAMockRequest = mock(ClientHttpRequest.class);
assertThatIllegalStateException()
.isThrownBy(() -> responseCreator.createResponse(notAMockRequest))
- .withMessage("Request should be an instance of MockClientHttpRequest");
+ .withMessage("Expected a MockClientHttpRequest");
}
@Test
void requestIsCopied() throws IOException {
- MockClientHttpRequest originalRequest = new MockClientHttpRequest(HttpMethod.POST,
- "https://example.org");
- String body = "original body";
+ MockClientHttpRequest originalRequest = new MockClientHttpRequest(HttpMethod.POST, "https://example.org");
originalRequest.getHeaders().add("X-example", "original");
- originalRequest.getBody().write(body.getBytes(StandardCharsets.UTF_8));
+ originalRequest.getBody().write("original body".getBytes(StandardCharsets.UTF_8));
+
MockClientHttpResponse originalResponse = new MockClientHttpResponse(new byte[0], 500);
List factoryRequests = new ArrayList<>();
ClientHttpRequestFactory originalFactory = (uri, httpMethod) -> {
@@ -99,8 +75,8 @@ class ExecutingResponseCreatorTests {
return request;
};
- final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator(originalFactory);
- final ClientHttpResponse response = responseCreator.createResponse(originalRequest);
+ ExecutingResponseCreator responseCreator = new ExecutingResponseCreator(originalFactory);
+ ClientHttpResponse response = responseCreator.createResponse(originalRequest);
assertThat(response).as("response").isSameAs(originalResponse);
assertThat(originalRequest.isExecuted()).as("originalRequest.isExecuted").isFalse();
@@ -109,16 +85,11 @@ class ExecutingResponseCreatorTests {
.hasSize(1)
.first()
.isNotSameAs(originalRequest)
- .satisfies(copiedRequest -> {
- assertThat(copiedRequest)
- .as("copied request")
- .isNotSameAs(originalRequest);
- assertThat(copiedRequest.isExecuted())
- .as("copiedRequest.isExecuted").isTrue();
- assertThat(copiedRequest.getBody())
- .as("copiedRequest.body").isNotSameAs(originalRequest.getBody());
- assertThat(copiedRequest.getHeaders())
- .as("copiedRequest.headers").isNotSameAs(originalRequest.getHeaders());
+ .satisfies(request -> {
+ assertThat(request).isNotSameAs(originalRequest);
+ assertThat(request.isExecuted()).isTrue();
+ assertThat(request.getBody()).isNotSameAs(originalRequest.getBody());
+ assertThat(request.getHeaders()).isNotSameAs(originalRequest.getHeaders());
});
}
}