Testing HTTP Streaming responses with SpringBoot

spring boot

Yesterday I wanted to test a REST endpoint written in SpringBoot. The particularity of this endpoint is that it return a Chunked Result.

When using SpringBoot, Http tests are really simplified with the usage of MockMvc:

@Test
public void whenGettingTheUserServiceThenReturnTheUsers() throws Exception {
    this.mockMvc.perform(get("/v1/bi/users/10"))
            .andExpect(status().isOk())
            .andExpect(content().string(tenUsers));
}

This works really well with synchronous requests, but when you use asynchronous request, the http response is not received when the andExpect are executed, and MockMvc receive this response:

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = {}
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

So you either created an endpoint returning a DeferredResponse or a response that stream some data in the http stream with the help of StreamingResponseBody

In the first case, you find many resources on the web showing you this method to await for the response in the test:

@Test
public void whenGettingTheUserDeferredServiceThenReturnTheUsers() throws Exception {
    MvcResult mvcResult = mockMvc.perform(get("/v1/bi/users/deferred/10"))
            .andReturn();
    mockMvc.perform(asyncDispatch(mvcResult))
            .andExpect(status().isOk())
            .andExpect(content().string(tenUsers));
}

And it works like a charm.

But I struggled to find the wait to test the endpoint not only deferring the response but also streaming the data. The solution above does not work as the response is still empty. Of course if you are streaming a huge file, the following test won’t help. But in the case of dynamic streaming, this can be helpful.

You must use MvcResult.getAsyncResult to fetch the whole body before your assertions:

@Test
public void whenGettingTheUserStreamedServiceThenReturnTheUsers() throws Exception {
    this.mockMvc.perform(get("/v1/bi/users/stream/10"))
            .andExpect(request().asyncStarted())
            .andDo(MvcResult::getAsyncResult)
            .andExpect(status().isOk())
            .andExpect(content().string(tenUsers));
}
comments powered by Disqus