Skip to content

Commit c702cd4

Browse files
committed
Polishing
See gh-29721
1 parent 4bcc243 commit c702cd4

File tree

5 files changed

+59
-102
lines changed

5 files changed

+59
-102
lines changed

framework-docs/src/docs/asciidoc/testing/spring-mvc-test-client.adoc

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,21 @@ logic but without running a server. The following example shows how to do so:
117117
// Test code that uses the above RestTemplate ...
118118
----
119119

120-
In the more specific cases where total isolation isn't desired and some integration testing
121-
of one or more calls is needed, a specific `ResponseCreator` can be set up in advance and
122-
used to perform actual requests and assert the response.
123-
The following example shows how to set up and use the `ExecutingResponseCreator` to do so:
120+
In some cases it may be necessary to perform an actual call to a remote service instead
121+
of mocking the response. The following example shows how to do that through
122+
`ExecutingResponseCreator`:
124123

125124
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
126125
.Java
127126
----
128127
RestTemplate restTemplate = new RestTemplate();
129128
130-
// Make sure to capture the request factory of the RestTemplate before binding
129+
// Create ExecutingResponseCreator with the original request factory
131130
ExecutingResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory());
132131
133132
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
134-
mockServer.expect(requestTo("/greeting")).andRespond(withActualResponse);
133+
mockServer.expect(requestTo("/profile")).andRespond(withSuccess());
134+
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse);
135135
136136
// Test code that uses the above RestTemplate ...
137137
@@ -142,7 +142,7 @@ The following example shows how to set up and use the `ExecutingResponseCreator`
142142
----
143143
val restTemplate = RestTemplate()
144144
145-
// Make sure to capture the request factory of the RestTemplate before binding
145+
// Create ExecutingResponseCreator with the original request factory
146146
val withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory())
147147
148148
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
@@ -156,16 +156,15 @@ The following example shows how to set up and use the `ExecutingResponseCreator`
156156

157157
In the preceding example, we create the `ExecutingResponseCreator` using the
158158
`ClientHttpRequestFactory` from the `RestTemplate` _before_ `MockRestServiceServer` replaces
159-
it with the custom one.
160-
Then we define expectations with two kinds of response:
159+
it with a different one that mocks responses.
160+
Then we define expectations with two kinds of responses:
161161

162162
* a stub `200` response for the `/profile` endpoint (no actual request will be executed)
163-
* an "executing response" for the `/quoteOfTheDay` endpoint
163+
* a response obtained through a call to the `/quoteOfTheDay` endpoint
164164

165-
In the second case, the request is executed by the `ClientHttpRequestFactory` that was
165+
In the second case, the request is executed through the `ClientHttpRequestFactory` that was
166166
captured earlier. This generates a response that could e.g. come from an actual remote server,
167-
depending on how the `RestTemplate` was originally configured, and MockMVC can be further
168-
used to assert the content of the response.
167+
depending on how the `RestTemplate` was originally configured.
169168

170169
[[spring-mvc-test-client-static-imports]]
171170
== Static Imports

spring-test/src/main/java/org/springframework/test/web/client/response/ExecutingResponseCreator.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,12 @@
2727
import org.springframework.util.StreamUtils;
2828

2929
/**
30-
* A {@code ResponseCreator} which delegates to a {@link ClientHttpRequestFactory}
31-
* to perform the request and return the associated response.
32-
* This is notably useful when testing code that calls multiple remote services, some
33-
* of which need to be actually called rather than further mocked.
34-
* <p>Note that the input request is asserted to be a {@code MockClientHttpRequest} and
35-
* the URI, method, headers and body are copied.
36-
* <p>The factory can typically be obtained from a {@code RestTemplate} but in case this
37-
* is used with e.g. {@code MockRestServiceServer}, make sure to capture the factory early
38-
* before binding the mock server to the RestTemplate (as it replaces the factory):
30+
* {@code ResponseCreator} that obtains the response by executing the request
31+
* through a {@link ClientHttpRequestFactory}. This is useful in scenarios with
32+
* multiple remote services where some need to be called rather than mocked.
33+
* <p>The {@code ClientHttpRequestFactory} is typically obtained from the
34+
* {@code RestTemplate} before it is passed to {@code MockRestServiceServer},
35+
* in effect using the original factory rather than the test factory:
3936
* <pre><code>
4037
* ResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate);
4138
* MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
@@ -53,7 +50,7 @@ public class ExecutingResponseCreator implements ResponseCreator {
5350

5451

5552
/**
56-
* Create a {@code ExecutingResponseCreator} from a {@code ClientHttpRequestFactory}.
53+
* Create an instance with the given {@code ClientHttpRequestFactory}.
5754
* @param requestFactory the request factory to delegate to
5855
*/
5956
public ExecutingResponseCreator(ClientHttpRequestFactory requestFactory) {
@@ -63,7 +60,7 @@ public ExecutingResponseCreator(ClientHttpRequestFactory requestFactory) {
6360

6461
@Override
6562
public ClientHttpResponse createResponse(ClientHttpRequest request) throws IOException {
66-
Assert.state(request instanceof MockClientHttpRequest, "Request should be an instance of MockClientHttpRequest");
63+
Assert.state(request instanceof MockClientHttpRequest, "Expected a MockClientHttpRequest");
6764
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
6865
ClientHttpRequest newRequest = this.requestFactory.createRequest(mockRequest.getURI(), mockRequest.getMethod());
6966
newRequest.getHeaders().putAll(mockRequest.getHeaders());

spring-test/src/main/java/org/springframework/test/web/client/response/MockRestResponseCreators.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,16 +28,11 @@
2828
import org.springframework.test.web.client.ResponseCreator;
2929

3030
/**
31-
* Static factory methods for obtaining a {@link ResponseCreator} instance.
31+
* Static factory methods to obtain a {@link ResponseCreator} with a fixed
32+
* response.
3233
*
33-
* <p><strong>Eclipse users:</strong> consider adding this class as a Java editor
34-
* favorite. To navigate, open the Preferences and type "favorites".
35-
*
36-
* <p>See also {@link ExecutingResponseCreator} for a {@code ResponseCreator} that is
37-
* capable of performing an actual request. That case is not offered as a factory method
38-
* here because of the early setup that is likely needed (capturing a request factory
39-
* which wouldn't be available anymore when the factory methods are typically invoked,
40-
* e.g. replaced in a {@code RestTemplate} by the {@code MockRestServiceServer}).
34+
* <p>In addition, see also the {@link ExecutingResponseCreator} implementation
35+
* that performs an actual requests to remote services.
4136
*
4237
* @author Rossen Stoyanchev
4338
* @since 3.2

spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -99,39 +99,34 @@ void ignoreExpectOrder() {
9999

100100
@Test
101101
void executingResponseCreator() {
102-
RestTemplate restTemplateWithMockEcho = createEchoRestTemplate();
102+
RestTemplate restTemplate = createEchoRestTemplate();
103+
ExecutingResponseCreator withActualCall = new ExecutingResponseCreator(restTemplate.getRequestFactory());
103104

104-
final ExecutingResponseCreator withActualCall = new ExecutingResponseCreator(restTemplateWithMockEcho.getRequestFactory());
105-
MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplateWithMockEcho).build();
105+
MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
106106
server.expect(requestTo("/profile")).andRespond(withSuccess());
107107
server.expect(requestTo("/quoteOfTheDay")).andRespond(withActualCall);
108108

109-
var response1 = restTemplateWithMockEcho.getForEntity("/profile", String.class);
110-
var response2 = restTemplateWithMockEcho.getForEntity("/quoteOfTheDay", String.class);
109+
var response1 = restTemplate.getForEntity("/profile", String.class);
110+
var response2 = restTemplate.getForEntity("/quoteOfTheDay", String.class);
111111
server.verify();
112112

113-
assertThat(response1.getStatusCode().value())
114-
.as("response1 status").isEqualTo(200);
115-
assertThat(response1.getBody())
116-
.as("response1 body").isNullOrEmpty();
117-
assertThat(response2.getStatusCode().value())
118-
.as("response2 status").isEqualTo(300);
119-
assertThat(response2.getBody())
120-
.as("response2 body").isEqualTo("echo from /quoteOfTheDay");
113+
assertThat(response1.getStatusCode().value()).isEqualTo(200);
114+
assertThat(response1.getBody()).isNullOrEmpty();
115+
assertThat(response2.getStatusCode().value()).isEqualTo(300);
116+
assertThat(response2.getBody()).isEqualTo("echo from /quoteOfTheDay");
121117
}
122118

123119
private static RestTemplate createEchoRestTemplate() {
124-
final ClientHttpRequestFactory echoRequestFactory = (uri, httpMethod) -> {
125-
final MockClientHttpRequest req = new MockClientHttpRequest(httpMethod, uri);
126-
String body = "echo from " + uri.getPath();
127-
final ClientHttpResponse resp = new MockClientHttpResponse(body.getBytes(StandardCharsets.UTF_8),
128-
// Instead of 200, we use a less-common status code on purpose
129-
HttpStatus.MULTIPLE_CHOICES);
130-
resp.getHeaders().setContentType(MediaType.TEXT_PLAIN);
131-
req.setResponse(resp);
132-
return req;
120+
ClientHttpRequestFactory requestFactory = (uri, httpMethod) -> {
121+
MockClientHttpRequest request = new MockClientHttpRequest(httpMethod, uri);
122+
ClientHttpResponse response = new MockClientHttpResponse(
123+
("echo from " + uri.getPath()).getBytes(StandardCharsets.UTF_8),
124+
HttpStatus.MULTIPLE_CHOICES); // use a different status on purpose
125+
response.getHeaders().setContentType(MediaType.TEXT_PLAIN);
126+
request.setResponse(response);
127+
return request;
133128
};
134-
return new RestTemplate(echoRequestFactory);
129+
return new RestTemplate(requestFactory);
135130
}
136131

137132
@Test

spring-test/src/test/java/org/springframework/test/web/client/response/ExecutingResponseCreatorTests.java

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,13 @@
1717
package org.springframework.test.web.client.response;
1818

1919
import java.io.IOException;
20-
import java.io.OutputStream;
21-
import java.net.URI;
2220
import java.nio.charset.StandardCharsets;
2321
import java.util.ArrayList;
2422
import java.util.List;
2523

2624
import org.junit.jupiter.api.Test;
2725

28-
import org.springframework.http.HttpHeaders;
2926
import org.springframework.http.HttpMethod;
30-
import org.springframework.http.client.AbstractClientHttpRequest;
3127
import org.springframework.http.client.ClientHttpRequest;
3228
import org.springframework.http.client.ClientHttpRequestFactory;
3329
import org.springframework.http.client.ClientHttpResponse;
@@ -36,6 +32,7 @@
3632

3733
import static org.assertj.core.api.Assertions.assertThat;
3834
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
35+
import static org.mockito.Mockito.mock;
3936

4037
/**
4138
* Tests for the {@link ExecutingResponseCreator} implementation.
@@ -46,50 +43,29 @@ class ExecutingResponseCreatorTests {
4643

4744
@Test
4845
void ensureRequestNotNull() {
49-
final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
46+
ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
5047

5148
assertThatIllegalStateException()
5249
.isThrownBy(() -> responseCreator.createResponse(null))
53-
.withMessage("Request should be an instance of MockClientHttpRequest");
50+
.withMessage("Expected a MockClientHttpRequest");
5451
}
5552

5653
@Test
5754
void ensureRequestIsMock() {
5855
final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator((uri, method) -> null);
59-
ClientHttpRequest notAMockRequest = new AbstractClientHttpRequest() {
60-
@Override
61-
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
62-
return null;
63-
}
64-
65-
@Override
66-
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
67-
return null;
68-
}
69-
70-
@Override
71-
public HttpMethod getMethod() {
72-
return null;
73-
}
74-
75-
@Override
76-
public URI getURI() {
77-
return null;
78-
}
79-
};
56+
ClientHttpRequest notAMockRequest = mock(ClientHttpRequest.class);
8057

8158
assertThatIllegalStateException()
8259
.isThrownBy(() -> responseCreator.createResponse(notAMockRequest))
83-
.withMessage("Request should be an instance of MockClientHttpRequest");
60+
.withMessage("Expected a MockClientHttpRequest");
8461
}
8562

8663
@Test
8764
void requestIsCopied() throws IOException {
88-
MockClientHttpRequest originalRequest = new MockClientHttpRequest(HttpMethod.POST,
89-
"https://example.org");
90-
String body = "original body";
65+
MockClientHttpRequest originalRequest = new MockClientHttpRequest(HttpMethod.POST, "https://example.org");
9166
originalRequest.getHeaders().add("X-example", "original");
92-
originalRequest.getBody().write(body.getBytes(StandardCharsets.UTF_8));
67+
originalRequest.getBody().write("original body".getBytes(StandardCharsets.UTF_8));
68+
9369
MockClientHttpResponse originalResponse = new MockClientHttpResponse(new byte[0], 500);
9470
List<MockClientHttpRequest> factoryRequests = new ArrayList<>();
9571
ClientHttpRequestFactory originalFactory = (uri, httpMethod) -> {
@@ -99,8 +75,8 @@ void requestIsCopied() throws IOException {
9975
return request;
10076
};
10177

102-
final ExecutingResponseCreator responseCreator = new ExecutingResponseCreator(originalFactory);
103-
final ClientHttpResponse response = responseCreator.createResponse(originalRequest);
78+
ExecutingResponseCreator responseCreator = new ExecutingResponseCreator(originalFactory);
79+
ClientHttpResponse response = responseCreator.createResponse(originalRequest);
10480

10581
assertThat(response).as("response").isSameAs(originalResponse);
10682
assertThat(originalRequest.isExecuted()).as("originalRequest.isExecuted").isFalse();
@@ -109,16 +85,11 @@ void requestIsCopied() throws IOException {
10985
.hasSize(1)
11086
.first()
11187
.isNotSameAs(originalRequest)
112-
.satisfies(copiedRequest -> {
113-
assertThat(copiedRequest)
114-
.as("copied request")
115-
.isNotSameAs(originalRequest);
116-
assertThat(copiedRequest.isExecuted())
117-
.as("copiedRequest.isExecuted").isTrue();
118-
assertThat(copiedRequest.getBody())
119-
.as("copiedRequest.body").isNotSameAs(originalRequest.getBody());
120-
assertThat(copiedRequest.getHeaders())
121-
.as("copiedRequest.headers").isNotSameAs(originalRequest.getHeaders());
88+
.satisfies(request -> {
89+
assertThat(request).isNotSameAs(originalRequest);
90+
assertThat(request.isExecuted()).isTrue();
91+
assertThat(request.getBody()).isNotSameAs(originalRequest.getBody());
92+
assertThat(request.getHeaders()).isNotSameAs(originalRequest.getHeaders());
12293
});
12394
}
12495
}

0 commit comments

Comments
 (0)