3

I have a unit test, where i am attempting to check the response of a async request being made, after converting the method to return a StreamingResponseBody using Spring 4.3.

The test method is below :

final MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac)
     .apply(SecurityMockMvcConfigurers.springSecurity())
     .build();

MvcResult mvcResult1 = mockMvc.perform(
   get("/reports/generic/100?FIELD1=&FIELD3=").headers(STANDARD_HEADERS.get()))
  .andExpect(status().isOk())
  .andExpect(request().asyncStarted())
  .andReturn();

mvcResult1.getAsyncResult();

mockMvc.perform(asyncDispatch(mvcResult1))
                    .andExpect(status().isOk())
                    .andExpect(content().contentType("text/csv"))
                    .andExpect(content().string("Test Data" + System.lineSeparator() + "FIELD1=" + System.lineSeparator() + "FIELD3=" + System.lineSeparator()))

The method it is calling looks like :

public StreamingResponseBody streamReport(@PathVariable("type") @NotNull String type, @PathVariable("id") @NotNull Long id, ReportConfiguration config, HttpServletResponse response) throws Exception {


    ReportServiceHandler handler = reportHandlerFactory.getHandler(type);
    final String reportFilename = handler.getReportFileName(id, reportConfiguration);

    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + reportFilename);
    response.setContentType("text/csv");

    return new StreamingResponseBody() {

        @Override
        public void writeTo(OutputStream outputStream) throws IOException {

            try {
                response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + reportFilename);
                response.setContentType("text/csv");
                ServletOutputStream out = (ServletOutputStream) outputStream;
                handler.generateReport(out, id, reportConfiguration);
                out.flush();
            } catch ( Exception e ) {
                response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "inline");
                response.setContentType("");
                throw new IOException(e);
            }
        }
    };
}

Debugging shows that the original request has the response from the async within it, but the async response object (within mvcResult1) is not being copied over during asyncDispatch so both the contextType and content string is null.

Is there a test configuration being missed here that handles async mvcResult so that the content can be asserted ?

Ben
  • 1,086
  • 3
  • 15
  • 30

1 Answers1

0

I have been quite successful with this. Here is what I do differently:

    return new StreamingResponseBody() {

    @Override
    public void writeTo(OutputStream outputStream) throws IOException {

        try {
            ... same
            out.flush();
        } catch ( Exception e ) {
            ... your error handling
        } finally {
            // Try to close the stream
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Throwable t) {
                // not much we can do
            }
        }
    }
};

Now, my tests look as such when made parallel to yours. When reading your code, I'm not sure why you call perform() twice. To me it's necessary only once.

//Same
final MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(wac)
   .apply(SecurityMockMvcConfigurers.springSecurity())
   .build();

// Same
MvcResult mvcResult1 = mockMvc.perform(
get(....))
  .andExpect(status().isOk())
  .andExpect(request().asyncStarted())
  .andReturn();
    
mvcResult1.getAsyncResult();

// From this point on, I have complete response,
// if it's not bugging you to place the entire file in memory
String thisIsYourFileContent = mvcResult1.getResponse().getContentAsString();

// Go on asserting the content of the file...

In the hope this helps someone.

Patrice Gagnon
  • 1,276
  • 14
  • 14