I'm using Spring 4.3.18 and Spring Boot 1.5.14 on Java 7.
I'm implementing a RestController endpoint which returns a DeferredResult
with a timeout callback. I'm trying to write a unit test for the timeout callback, but I can't get a MockMvc
unit test to call the timeout callback.
For the sake of testing, I wrote this endpoint:
@PostMapping("/test")
public DeferredResult<String>
testit() {
logger.info("testit called");
final DeferredResult<String> rv = new DeferredResult<>(1000L);
rv.onTimeout(new Runnable() {
@Override
public void run() {
logger.info("run called");
rv.setResult("timed out");
}
});
return rv;
}
and this unit test:
@Autowired
private MockMvc mockMvc;
@Test
public void testTest() throws Exception {
MvcResult result = mockMvc.perform(post("/rest/tasks/test"))
.andExpect(request().asyncStarted())
.andReturn();
result.getAsyncResult(1500);
mockMvc.perform(asyncDispatch(result))
.andExpect(status().isOk())
;
}
(The call to result.getAsyncResult(1500)
is based on https://jira.spring.io/browse/SPR-16869)
When I run this, the testit()
endpoint is called, and after a 1500 ms delay, I get an exception complaining that setResult()
was never called. The timeout handler isn't invoked:
java.lang.IllegalStateException: Async result for handler [public org.springframework.web.context.request.async.DeferredResult<java.lang.String> my.package.TasksController.testit()] was not set during the specified timeToWait=1500
at org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:135)
at my.package.TasksControllerTest.testTest(TasksControllerTest.java:200)
2517 [main] INFO my.package.TasksController - testit called
MockHttpServletRequest:
HTTP Method = POST
Request URI = /rest/tasks/test
Parameters = {}
Headers = {}
Handler:
Type = my.package.TasksController
Method = public org.springframework.web.context.request.async.DeferredResult<java.lang.String> my.package.TasksController.testit()
Async:
Async started = true
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
4037 [Thread-2] INFO org.springframework.web.context.support.GenericWebApplicationContext - Closing org.springframework.web.context.support.GenericWebApplicationContext@6ea91f6: startup date [Thu Jul 26 19:44:31 EDT 2018]; root of context hierarchy
What do I need to do to make the test framework invoke the timeout handler on the DeferredResult?
It seems it's possible to create a synthetic timeout in the unit test like this:
Accessing the request's async listeners and calling
onTimeout()
will result in calling theDeferredRequest
's timeout callback.The call to
result.getAsyncResult(1500)
from my question is redundant for this test, becauseasyncDispatch()
will callgetAsyncResult()
anyway.