Improve support of async request in MockMvcTester
This commit improves the handling of asynchronous requests by offering a way to opt-in for the raw async result. This provides first class support for asserting a request that might still be in process as well as the asyncResult, if necessary. See gh-33040
This commit is contained in:
@@ -55,6 +55,7 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.servlet.ResultMatcher;
|
||||
import org.springframework.test.web.servlet.assertj.MockMvcTester.MockMultipartMvcRequestBuilder;
|
||||
import org.springframework.test.web.servlet.assertj.MockMvcTester.MockMvcRequestBuilder;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.Errors;
|
||||
@@ -105,6 +106,9 @@ public class MockMvcTesterIntegrationTests {
|
||||
@Nested
|
||||
class PerformTests {
|
||||
|
||||
private final MockMultipartFile file = new MockMultipartFile("file", "content.txt", null,
|
||||
"value".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
@Test
|
||||
void syncRequestWithDefaultExchange() {
|
||||
assertThat(mvc.get().uri("/greet")).hasStatusOk();
|
||||
@@ -116,6 +120,13 @@ public class MockMvcTesterIntegrationTests {
|
||||
.hasBodyTextEqualTo("name=Joe&someBoolean=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asyncMultipartRequestWithDefaultExchange() {
|
||||
assertThat(mvc.post().uri("/multipart-streaming").multipart()
|
||||
.file(this.file).param("timeToWait", "100"))
|
||||
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncRequestWithExplicitExchange() {
|
||||
assertThat(mvc.get().uri("/greet").exchange()).hasStatusOk();
|
||||
@@ -127,6 +138,13 @@ public class MockMvcTesterIntegrationTests {
|
||||
.hasStatusOk().hasBodyTextEqualTo("name=Joe&someBoolean=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asyncMultipartRequestWitExplicitExchange() {
|
||||
assertThat(mvc.post().uri("/multipart-streaming").multipart()
|
||||
.file(this.file).param("timeToWait", "100").exchange())
|
||||
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncRequestWithExplicitExchangeIgnoresDuration() {
|
||||
Duration timeToWait = mock(Duration.class);
|
||||
@@ -140,6 +158,13 @@ public class MockMvcTesterIntegrationTests {
|
||||
.hasStatusOk().hasBodyTextEqualTo("name=Joe&someBoolean=true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asyncMultipartRequestWithExplicitExchangeAndEnoughTimeToWait() {
|
||||
assertThat(mvc.post().uri("/multipart-streaming").multipart()
|
||||
.file(this.file).param("timeToWait", "100").exchange(Duration.ofMillis(200)))
|
||||
.hasStatusOk().hasBodyTextEqualTo("name=Joe&file=content.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asyncRequestWithExplicitExchangeAndNotEnoughTimeToWait() {
|
||||
MockMvcRequestBuilder builder = mvc.get().uri("/streaming").param("timeToWait", "500");
|
||||
@@ -147,6 +172,15 @@ public class MockMvcTesterIntegrationTests {
|
||||
.isThrownBy(() -> builder.exchange(Duration.ofMillis(100)))
|
||||
.withMessageContaining("was not set during the specified timeToWait=100");
|
||||
}
|
||||
|
||||
@Test
|
||||
void asyncMultipartRequestWithExplicitExchangeAndNotEnoughTimeToWait() {
|
||||
MockMultipartMvcRequestBuilder builder = mvc.post().uri("/multipart-streaming").multipart()
|
||||
.file(this.file).param("timeToWait", "500");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> builder.exchange(Duration.ofMillis(100)))
|
||||
.withMessageContaining("was not set during the specified timeToWait=100");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
@@ -154,14 +188,13 @@ public class MockMvcTesterIntegrationTests {
|
||||
|
||||
@Test
|
||||
void hasAsyncStartedTrue() {
|
||||
// Need #perform as the regular exchange waits for async completion automatically
|
||||
assertThat(mvc.perform(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON)))
|
||||
assertThat(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON).asyncExchange())
|
||||
.request().hasAsyncStarted(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAsyncStartedFalse() {
|
||||
assertThat(mvc.get().uri("/greet")).request().hasAsyncStarted(false);
|
||||
assertThat(mvc.get().uri("/greet").asyncExchange()).request().hasAsyncStarted(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -325,8 +358,7 @@ public class MockMvcTesterIntegrationTests {
|
||||
|
||||
@Test
|
||||
void asyncResult() {
|
||||
// Need #perform as the regular exchange waits for async completion automatically
|
||||
MvcTestResult result = mvc.perform(mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON));
|
||||
MvcTestResult result = mvc.get().uri("/callable").accept(MediaType.APPLICATION_JSON).asyncExchange();
|
||||
assertThat(result.getMvcResult().getAsyncResult())
|
||||
.asInstanceOf(InstanceOfAssertFactories.map(String.class, Object.class))
|
||||
.containsOnly(entry("key", "value"));
|
||||
@@ -694,9 +726,24 @@ public class MockMvcTesterIntegrationTests {
|
||||
}
|
||||
|
||||
@PutMapping("/multipart-put")
|
||||
public ModelAndView multiPartViaHttpPut(@RequestParam MultipartFile file) {
|
||||
ModelAndView multiPartViaHttpPut(@RequestParam MultipartFile file) {
|
||||
return new ModelAndView("index", Map.of("name", file.getName()));
|
||||
}
|
||||
|
||||
@PostMapping("/multipart-streaming")
|
||||
StreamingResponseBody streaming(@RequestParam MultipartFile file, @RequestParam long timeToWait) {
|
||||
return out -> {
|
||||
PrintStream stream = new PrintStream(out, true, StandardCharsets.UTF_8);
|
||||
stream.print("name=Joe");
|
||||
try {
|
||||
Thread.sleep(timeToWait);
|
||||
stream.print("&file=" + file.getOriginalFilename());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
/* no-op */
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.web.Person;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.RequestBuilder;
|
||||
import org.springframework.test.web.servlet.assertj.MockMvcTester;
|
||||
import org.springframework.test.web.servlet.assertj.MvcTestResult;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
@@ -265,9 +264,7 @@ class AsyncTests {
|
||||
void printAsyncResult() {
|
||||
StringWriter asyncWriter = new StringWriter();
|
||||
|
||||
// Use #perform to not complete asynchronous request automatically
|
||||
RequestBuilder requestBuilder = this.mockMvc.get().uri("/1").param("deferredResult", "true");
|
||||
MvcTestResult result = this.mockMvc.perform(requestBuilder);
|
||||
MvcTestResult result = this.mockMvc.get().uri("/1").param("deferredResult", "true").asyncExchange();
|
||||
assertThat(result).debug(asyncWriter).request().hasAsyncStarted(true);
|
||||
assertThat(asyncWriter.toString()).contains("Async started = true");
|
||||
asyncWriter = new StringWriter(); // Reset
|
||||
|
||||
Reference in New Issue
Block a user