Skip to content

Commit 1eaa9cd

Browse files
committed
Polish
1 parent 17abb4d commit 1eaa9cd

File tree

3 files changed

+149
-44
lines changed

3 files changed

+149
-44
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/assertj/MockMvcTester.java

+58-35
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@
6161
* builder -> builder.addFilters(filter).build());
6262
* </code></pre>
6363
*
64-
* <p>A tester can be created in standalone mode by providing the controller(s)
65-
* to include:<pre><code class="java">
64+
* <p>A tester can be created in standalone mode by providing the controller
65+
* instances to include:<pre><code class="java">
6666
* // Create an instance for PersonController
6767
* MockMvcTester mvc = MockMvcTester.of(new PersonController());
6868
* </code></pre>
@@ -74,7 +74,7 @@
7474
* assertThat(mvc.get().uri("/hi")).hasStatusOk().hasBodyTextEqualTo("Hello");
7575
* </code></pre>
7676
*
77-
*<p>For more complex scenarios the {@linkplain MvcTestResult result} of the
77+
* <p>For more complex scenarios the {@linkplain MvcTestResult result} of the
7878
* exchange can be assigned in a variable to run multiple assertions:
7979
* <pre><code class="java">
8080
* // perform a POST on /save and assert the response body is empty
@@ -83,13 +83,27 @@
8383
* assertThat(result).body().isEmpty();
8484
* </code></pre>
8585
*
86+
* <p>If the request is processing asynchronously, {@code exchange} waits for
87+
* its completion, either using the
88+
* {@linkplain org.springframework.mock.web.MockAsyncContext#setTimeout default
89+
* timeout} or a given one. If you prefer to get the result of an
90+
* asynchronous request immediately, use {@code asyncExchange}:
91+
* <pre><code class="java">
92+
* // perform a POST on /save and assert an asynchronous request has started
93+
* assertThat(mvc.post().uri("/save").asyncExchange()).request().hasAsyncStarted();
94+
* </code></pre>
95+
*
8696
* <p>You can also perform requests using the static builders approach that
8797
* {@link MockMvc} uses. For instance:<pre><code class="java">
8898
* // perform a GET on /hi and assert the response body is equal to Hello
8999
* assertThat(mvc.perform(get("/hi")))
90100
* .hasStatusOk().hasBodyTextEqualTo("Hello");
91101
* </code></pre>
92102
*
103+
* <p>Use this approach if you have a custom {@link RequestBuilder} implementation
104+
* that you'd like to integrate here. This approach is also invoking {@link MockMvc}
105+
* without any additional processing of asynchronous requests.
106+
*
93107
* <p>One main difference between {@link MockMvc} and {@code MockMvcTester} is
94108
* that an unresolved exception is not thrown directly when using
95109
* {@code MockMvcTester}. Rather an {@link MvcTestResult} is available with an
@@ -231,8 +245,10 @@ public MockMvcTester withHttpMessageConverters(Iterable<HttpMessageConverter<?>>
231245
* Prepare an HTTP GET request.
232246
* <p>The returned builder can be wrapped in {@code assertThat} to enable
233247
* assertions on the result. For multi-statements assertions, use
234-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
235-
* result.
248+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
249+
* result. To control the time to wait for asynchronous request to complete
250+
* on a per-request basis, use
251+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
236252
* @return a request builder for specifying the target URI
237253
*/
238254
public MockMvcRequestBuilder get() {
@@ -243,8 +259,10 @@ public MockMvcRequestBuilder get() {
243259
* Prepare an HTTP HEAD request.
244260
* <p>The returned builder can be wrapped in {@code assertThat} to enable
245261
* assertions on the result. For multi-statements assertions, use
246-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
247-
* result.
262+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
263+
* result. To control the time to wait for asynchronous request to complete
264+
* on a per-request basis, use
265+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
248266
* @return a request builder for specifying the target URI
249267
*/
250268
public MockMvcRequestBuilder head() {
@@ -255,8 +273,10 @@ public MockMvcRequestBuilder head() {
255273
* Prepare an HTTP POST request.
256274
* <p>The returned builder can be wrapped in {@code assertThat} to enable
257275
* assertions on the result. For multi-statements assertions, use
258-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
259-
* result.
276+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
277+
* result. To control the time to wait for asynchronous request to complete
278+
* on a per-request basis, use
279+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
260280
* @return a request builder for specifying the target URI
261281
*/
262282
public MockMvcRequestBuilder post() {
@@ -267,8 +287,10 @@ public MockMvcRequestBuilder post() {
267287
* Prepare an HTTP PUT request.
268288
* <p>The returned builder can be wrapped in {@code assertThat} to enable
269289
* assertions on the result. For multi-statements assertions, use
270-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
271-
* result.
290+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
291+
* result. To control the time to wait for asynchronous request to complete
292+
* on a per-request basis, use
293+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
272294
* @return a request builder for specifying the target URI
273295
*/
274296
public MockMvcRequestBuilder put() {
@@ -279,8 +301,10 @@ public MockMvcRequestBuilder put() {
279301
* Prepare an HTTP PATCH request.
280302
* <p>The returned builder can be wrapped in {@code assertThat} to enable
281303
* assertions on the result. For multi-statements assertions, use
282-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
283-
* result.
304+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
305+
* result. To control the time to wait for asynchronous request to complete
306+
* on a per-request basis, use
307+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
284308
* @return a request builder for specifying the target URI
285309
*/
286310
public MockMvcRequestBuilder patch() {
@@ -291,8 +315,10 @@ public MockMvcRequestBuilder patch() {
291315
* Prepare an HTTP DELETE request.
292316
* <p>The returned builder can be wrapped in {@code assertThat} to enable
293317
* assertions on the result. For multi-statements assertions, use
294-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
295-
* result.
318+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
319+
* result. To control the time to wait for asynchronous request to complete
320+
* on a per-request basis, use
321+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
296322
* @return a request builder for specifying the target URI
297323
*/
298324
public MockMvcRequestBuilder delete() {
@@ -303,8 +329,10 @@ public MockMvcRequestBuilder delete() {
303329
* Prepare an HTTP OPTIONS request.
304330
* <p>The returned builder can be wrapped in {@code assertThat} to enable
305331
* assertions on the result. For multi-statements assertions, use
306-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
307-
* result.
332+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
333+
* result. To control the time to wait for asynchronous request to complete
334+
* on a per-request basis, use
335+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
308336
* @return a request builder for specifying the target URI
309337
*/
310338
public MockMvcRequestBuilder options() {
@@ -315,44 +343,39 @@ public MockMvcRequestBuilder options() {
315343
* Prepare a request for the specified {@code HttpMethod}.
316344
* <p>The returned builder can be wrapped in {@code assertThat} to enable
317345
* assertions on the result. For multi-statements assertions, use
318-
* {@linkplain MockMvcRequestBuilder#exchange() exchange} to assign the
319-
* result.
346+
* {@link MockMvcRequestBuilder#exchange() exchange()} to assign the
347+
* result. To control the time to wait for asynchronous request to complete
348+
* on a per-request basis, use
349+
* {@link MockMvcRequestBuilder#exchange(Duration) exchange(Duration)}.
320350
* @return a request builder for specifying the target URI
321351
*/
322352
public MockMvcRequestBuilder method(HttpMethod method) {
323353
return new MockMvcRequestBuilder(method);
324354
}
325355

326356
/**
327-
* Perform a request using {@link MockMvcRequestBuilders} and return a
357+
* Perform a request using the given {@link RequestBuilder} and return a
328358
* {@link MvcTestResult result} that can be used with standard
329359
* {@link org.assertj.core.api.Assertions AssertJ} assertions.
330-
* <p>Use static methods of {@link MockMvcRequestBuilders} to prepare the
331-
* request, wrapping the invocation in {@code assertThat}. The following
332-
* asserts that a {@linkplain MockMvcRequestBuilders#get(URI) GET} request
333-
* against "/greet" has an HTTP status code 200 (OK) and a simple body:
334-
* <pre><code class="java">assertThat(mvc.perform(get("/greet")))
335-
* .hasStatusOk()
336-
* .body().asString().isEqualTo("Hello");
337-
* </code></pre>
360+
* <p>Use only this method if you need to provide a custom
361+
* {@link RequestBuilder}. For regular cases, users should initiate the
362+
* configuration of the request using one of the methods available on
363+
* this instance, e.g. {@link #get()} for HTTP GET.
338364
* <p>Contrary to {@link MockMvc#perform(RequestBuilder)}, this does not
339365
* throw an exception if the request fails with an unresolved exception.
340366
* Rather, the result provides the exception, if any. Assuming that a
341367
* {@link MockMvcRequestBuilders#post(URI) POST} request against
342368
* {@code /boom} throws an {@code IllegalStateException}, the following
343369
* asserts that the invocation has indeed failed with the expected error
344370
* message:
345-
* <pre><code class="java">assertThat(mvc.perform(post("/boom")))
346-
* .unresolvedException().isInstanceOf(IllegalStateException.class)
371+
* <pre><code class="java">assertThat(mvc.post().uri("/boom")))
372+
* .failure().isInstanceOf(IllegalStateException.class)
347373
* .hasMessage("Expected");
348374
* </code></pre>
349-
* @param requestBuilder used to prepare the request to execute;
350-
* see static factory methods in
351-
* {@link org.springframework.test.web.servlet.request.MockMvcRequestBuilders}
375+
* @param requestBuilder used to prepare the request to execute
352376
* @return an {@link MvcTestResult} to be wrapped in {@code assertThat}
353377
* @see MockMvc#perform(RequestBuilder)
354-
* @see #get()
355-
* @see #post()
378+
* @see #method(HttpMethod)
356379
*/
357380
public MvcTestResult perform(RequestBuilder requestBuilder) {
358381
Object result = getMvcResultOrFailure(requestBuilder);

spring-test/src/test/java/org/springframework/test/web/servlet/assertj/MockMvcTesterIntegrationTests.java

+22-9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
@SpringJUnitWebConfig
9898
public class MockMvcTesterIntegrationTests {
9999

100+
private static final MockMultipartFile file = new MockMultipartFile("file", "content.txt", null,
101+
"value".getBytes(StandardCharsets.UTF_8));
102+
103+
100104
private final MockMvcTester mvc;
101105

102106
MockMvcTesterIntegrationTests(WebApplicationContext wac) {
@@ -106,9 +110,6 @@ public class MockMvcTesterIntegrationTests {
106110
@Nested
107111
class PerformTests {
108112

109-
private final MockMultipartFile file = new MockMultipartFile("file", "content.txt", null,
110-
"value".getBytes(StandardCharsets.UTF_8));
111-
112113
@Test
113114
void syncRequestWithDefaultExchange() {
114115
assertThat(mvc.get().uri("/greet")).hasStatusOk();
@@ -123,7 +124,7 @@ void asyncRequestWithDefaultExchange() {
123124
@Test
124125
void asyncMultipartRequestWithDefaultExchange() {
125126
assertThat(mvc.post().uri("/multipart-streaming").multipart()
126-
.file(this.file).param("timeToWait", "100"))
127+
.file(file).param("timeToWait", "100"))
127128
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
128129
}
129130

@@ -141,7 +142,7 @@ void asyncRequestWithExplicitExchange() {
141142
@Test
142143
void asyncMultipartRequestWitExplicitExchange() {
143144
assertThat(mvc.post().uri("/multipart-streaming").multipart()
144-
.file(this.file).param("timeToWait", "100").exchange())
145+
.file(file).param("timeToWait", "100").exchange())
145146
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
146147
}
147148

@@ -161,7 +162,7 @@ void asyncRequestWithExplicitExchangeAndEnoughTimeToWait() {
161162
@Test
162163
void asyncMultipartRequestWithExplicitExchangeAndEnoughTimeToWait() {
163164
assertThat(mvc.post().uri("/multipart-streaming").multipart()
164-
.file(this.file).param("timeToWait", "100").exchange(Duration.ofMillis(200)))
165+
.file(file).param("timeToWait", "100").exchange(Duration.ofMillis(200)))
165166
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
166167
}
167168

@@ -176,7 +177,7 @@ void asyncRequestWithExplicitExchangeAndNotEnoughTimeToWait() {
176177
@Test
177178
void asyncMultipartRequestWithExplicitExchangeAndNotEnoughTimeToWait() {
178179
MockMultipartMvcRequestBuilder builder = mvc.post().uri("/multipart-streaming").multipart()
179-
.file(this.file).param("timeToWait", "500");
180+
.file(file).param("timeToWait", "500");
180181
assertThatIllegalStateException()
181182
.isThrownBy(() -> builder.exchange(Duration.ofMillis(100)))
182183
.withMessageContaining("was not set during the specified timeToWait=100");
@@ -192,11 +193,24 @@ void hasAsyncStartedTrue() {
192193
.request().hasAsyncStarted(true);
193194
}
194195

196+
@Test
197+
void hasAsyncStartedForMultipartTrue() {
198+
assertThat(mvc.post().uri("/multipart-streaming").multipart()
199+
.file(file).param("timeToWait", "100").asyncExchange())
200+
.request().hasAsyncStarted(true);
201+
}
202+
195203
@Test
196204
void hasAsyncStartedFalse() {
197205
assertThat(mvc.get().uri("/greet").asyncExchange()).request().hasAsyncStarted(false);
198206
}
199207

208+
@Test
209+
void hasAsyncStartedForMultipartFalse() {
210+
assertThat(mvc.put().uri("/multipart-put").multipart().file(file).asyncExchange())
211+
.request().hasAsyncStarted(false);
212+
}
213+
200214
@Test
201215
void attributes() {
202216
assertThat(mvc.get().uri("/greet")).request().attributes()
@@ -220,8 +234,7 @@ class MultipartTests {
220234

221235
@Test
222236
void multipartWithPut() {
223-
MockMultipartFile part = new MockMultipartFile("file", "content.txt", null, "value".getBytes(StandardCharsets.UTF_8));
224-
assertThat(mvc.put().uri("/multipart-put").multipart().file(part).file(JSON_PART_FILE))
237+
assertThat(mvc.put().uri("/multipart-put").multipart().file(file).file(JSON_PART_FILE))
225238
.hasStatusOk()
226239
.hasViewName("index")
227240
.model().contains(entry("name", "file"));

spring-test/src/test/java/org/springframework/test/web/servlet/assertj/MockMvcTesterTests.java

+69
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,25 @@
1919
import java.util.List;
2020
import java.util.Map;
2121
import java.util.concurrent.atomic.AtomicInteger;
22+
import java.util.function.Consumer;
2223

2324
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import jakarta.servlet.ServletContext;
2426
import jakarta.servlet.ServletException;
2527
import org.junit.jupiter.api.Test;
2628

29+
import org.springframework.cglib.core.internal.Function;
2730
import org.springframework.context.annotation.AnnotationConfigUtils;
2831
import org.springframework.context.annotation.Bean;
2932
import org.springframework.context.annotation.Configuration;
33+
import org.springframework.http.HttpMethod;
3034
import org.springframework.http.HttpStatus;
3135
import org.springframework.http.MediaType;
3236
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
37+
import org.springframework.mock.web.MockHttpServletRequest;
3338
import org.springframework.mock.web.MockServletContext;
3439
import org.springframework.test.json.AbstractJsonContentAssert;
40+
import org.springframework.test.web.servlet.assertj.MockMvcTester.MockMvcRequestBuilder;
3541
import org.springframework.web.bind.annotation.GetMapping;
3642
import org.springframework.web.bind.annotation.PostMapping;
3743
import org.springframework.web.bind.annotation.RestController;
@@ -57,6 +63,8 @@ class MockMvcTesterTests {
5763
private static final MappingJackson2HttpMessageConverter jsonHttpMessageConverter =
5864
new MappingJackson2HttpMessageConverter(new ObjectMapper());
5965

66+
private final ServletContext servletContext = new MockServletContext();
67+
6068

6169
@Test
6270
void createShouldRejectNullMockMvc() {
@@ -139,6 +147,67 @@ void performWithUnresolvedExceptionSetsException() {
139147
assertThat(result).hasFieldOrPropertyWithValue("mvcResult", null);
140148
}
141149

150+
@Test
151+
void getConfiguresBuilder() {
152+
assertThat(createMockHttpServletRequest(tester -> tester.get().uri("/hello")))
153+
.satisfies(hasSettings(HttpMethod.GET, "/hello"));
154+
}
155+
156+
@Test
157+
void headConfiguresBuilder() {
158+
assertThat(createMockHttpServletRequest(tester -> tester.head().uri("/download")))
159+
.satisfies(hasSettings(HttpMethod.HEAD, "/download"));
160+
}
161+
162+
@Test
163+
void postConfiguresBuilder() {
164+
assertThat(createMockHttpServletRequest(tester -> tester.post().uri("/save")))
165+
.satisfies(hasSettings(HttpMethod.POST, "/save"));
166+
}
167+
168+
@Test
169+
void putConfiguresBuilder() {
170+
assertThat(createMockHttpServletRequest(tester -> tester.put().uri("/save")))
171+
.satisfies(hasSettings(HttpMethod.PUT, "/save"));
172+
}
173+
174+
@Test
175+
void patchConfiguresBuilder() {
176+
assertThat(createMockHttpServletRequest(tester -> tester.patch().uri("/update")))
177+
.satisfies(hasSettings(HttpMethod.PATCH, "/update"));
178+
}
179+
180+
@Test
181+
void deleteConfiguresBuilder() {
182+
assertThat(createMockHttpServletRequest(tester -> tester.delete().uri("/users/42")))
183+
.satisfies(hasSettings(HttpMethod.DELETE, "/users/42"));
184+
}
185+
186+
@Test
187+
void optionsConfiguresBuilder() {
188+
assertThat(createMockHttpServletRequest(tester -> tester.options().uri("/users")))
189+
.satisfies(hasSettings(HttpMethod.OPTIONS, "/users"));
190+
}
191+
192+
@Test
193+
void methodConfiguresBuilderWithCustomMethod() {
194+
HttpMethod customMethod = HttpMethod.valueOf("CUSTOM");
195+
assertThat(createMockHttpServletRequest(tester -> tester.method(customMethod).uri("/hello")))
196+
.satisfies(hasSettings(customMethod, "/hello"));
197+
}
198+
199+
private MockHttpServletRequest createMockHttpServletRequest(Function<MockMvcTester, MockMvcRequestBuilder> builder) {
200+
MockMvcTester mockMvcTester = MockMvcTester.of(HelloController.class);
201+
return builder.apply(mockMvcTester).buildRequest(this.servletContext);
202+
}
203+
204+
private Consumer<MockHttpServletRequest> hasSettings(HttpMethod method, String uri) {
205+
return request -> {
206+
assertThat(request.getMethod()).isEqualTo(method.name());
207+
assertThat(request.getRequestURI()).isEqualTo(uri);
208+
};
209+
}
210+
142211
private GenericWebApplicationContext create(Class<?>... classes) {
143212
GenericWebApplicationContext applicationContext = new GenericWebApplicationContext(new MockServletContext());
144213
AnnotationConfigUtils.registerAnnotationConfigProcessors(applicationContext);

0 commit comments

Comments
 (0)