Testing Spring asyncResult() and jsonPath() together
json, spring-mvc, spring-test, spring-test-mvc
Solution
Bud's answer really helped point me in the right direction however it didn't quite work because it did not wait for the async result. Since posting this question, the spring-mvc-showcase samples (https://github.com/SpringSource/spring-mvc-showcase) have been updated.
It seems like in the first part of the call when you retrieve the MvcResult, you need to assert on an asyncResult() and in the case of JSON pojo mapping you need to assert on the actual type itself (not JSON). So I needed to add the third line below to Bud's answer, then the rest just works.
MvcResult mvcResult = this.mockMvc.perform(get("/trigger/job/xyz"))
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(instanceOf(TriggerResult.class)))
.andReturn();
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("status").value("SUCCESS"))
.andExpect(jsonPath("message").value("A meaningful message appears"));
Note: `instanceOf()` is `org.hamcrest.CoreMatchers.instanceOf`. To get access to Hamcrest libraries include the latest `hamcrest-library` jar.
For maven ...
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>LATEST VERSION HERE</version>
<scope>test</scope>
</dependency>
Problem
I'm using a restful url to kick off a long-running backend process (it is normally on a cron schedule, but we want the ability to kick it off manually). The code below works and I see the result in the browser when I test manually. ``` @ResponseBody @RequestMapping(value = "/trigger/{jobName}", method = RequestMethod.GET) public Callable<TriggerResult> triggerJob(@PathVariable final String jobName) { return new Callable<TriggerResult>() { @Override public TriggerResult call() throws Exception { // Code goes here to locate relevant job and kick it off, waiting for result String message = <result from my job>; return new TriggerResult(SUCCESS, message); } }; } ``` When I test this without the `Callable` I used the code below and it all works (I changed the expected error message to simplify post). ``` mockMvc.perform(get("/trigger/job/xyz")) .andExpect(status().isOk()) .andDo(print()) .andExpect(jsonPath("status").value("SUCCESS")) .andExpect(jsonPath("message").value("A meaningful message appears")); ``` When I added the `Callable` however it does not work. I also tried below but it did not work. Anyone else had success? ``` mockMvc.perform(get("/trigger/job/xyz")) .andExpect(status().isOk()) .andDo(print()) .andExpect(request().asyncResult(jsonPath("status").value("SUCCESS"))) .andExpect(request().asyncResult(jsonPath("message").value("A meaningful message appears"))); ``` Below is the relevant part from my print(). Looks like mockMvc can't untangle the Json correctly in this case (even though it works in my browser)? When I do this without `Callable` I see full JSON. ``` MockHttpServletRequest: HTTP Method = GET Request URI = /trigger/job/xyz Parameters = {} Headers = {} Handler: Type = foo.bar.web.controller.TriggerJobController Method = public java.util.concurrent.Callable<foo.bar.myproject.web.model.TriggerResult> foo.bar.myproject.web.controller.TriggerJobController.triggerJob(java.lang.String) Async: Was async started = true Async result = foo.bar.myproject.web.model.TriggerResult@67aa1e71 Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: MockHttpServletResponse: Status = 200 Error message = null Headers = {} Content type = null Body = Forwarded URL = null Redirected URL = null Cookies = [] ```