Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-16869

MockMvc - IllegalStateException: Async result for handler was not set during specified timeToWait=-1

    Details

    • Last commented by a User:
      false

      Description

      When trying to test Server-Sent Events endpoint created using rxjava2 and spring's ReactiveTypeHandler you encounter:

      java.lang.IllegalStateException: Async result for handler [io.reactivex.Flowable<java.lang.String> com.example.asyncssebug.MockMvcAsyncBugTest$TestApp.sse()] was not set during the specified timeToWait=-1java.lang.IllegalStateException: Async result for handler [io.reactivex.Flowable<java.lang.String> com.example.asyncssebug.MockMvcAsyncBugTest$TestApp.sse()] was not set during the specified timeToWait=-1
       at org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:145) at org.springframework.test.web.servlet.DefaultMvcResult.getAsyncResult(DefaultMvcResult.java:136) at org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch(MockMvcRequestBuilders.java:269)
      

      Example test code to reproduce bug along with walkaround:

      package com.example.asyncssebug;
      
      import io.reactivex.Flowable;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
      import org.springframework.boot.test.context.SpringBootTest;
      import org.springframework.http.MediaType;
      import org.springframework.test.context.junit4.SpringRunner;
      import org.springframework.test.web.servlet.MockMvc;
      import org.springframework.test.web.servlet.MvcResult;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import java.awt.*;
      import java.util.concurrent.TimeUnit;
      
      import static org.hamcrest.Matchers.nullValue;
      import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
      import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
      import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
      import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
      import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
      
      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = MockMvcAsyncBugTest.TestApp.class)
      @AutoConfigureMockMvc
      public class MockMvcAsyncBugTest {
      
          @RestController
          @RequestMapping("/events")
          @SpringBootApplication
          public static class TestApp {
      
              @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
              Flowable<String> sse() {
                  return Flowable.intervalRange(0, 3, 0, 1, TimeUnit.SECONDS)
                          .map(aLong -> String.format("event%d", aLong));
              }
          }
      
          @Autowired
          MockMvc mockMvc;
      
          @Test
          public void failsWithIllegalStateExceptionAsyncResultForHandlerWasNotSetDuringSpecifiedTimeToWait() throws Exception {
              MvcResult mvcResult = mockMvc.perform(get("/events"))
                      .andExpect(request().asyncStarted())
                      .andExpect(status().isOk())
                      .andReturn();
      
              mockMvc.perform(asyncDispatch(mvcResult))
                      .andExpect(content().string("data:event0\n\ndata:event1\n\ndata:event2\n\n"));
          }
      
          @Test
          public void alsoFailsWithIllegalStateExceptionAsyncResultForHandlerWasNotSetDuringSpecifiedTimeToWait() throws Exception {
              mockMvc.perform(get("/events"))
                      .andExpect(request().asyncStarted())
                      .andExpect(request().asyncResult(nullValue()))
                      .andExpect(status().isOk())
                      .andExpect(content().string("data:event0\n\ndata:event1\n\ndata:event2\n\n"))
                      .andReturn();
          }
      
          @Test
          public void walkaroundToMakeItWork() throws Exception {
              MvcResult mvcResult = mockMvc.perform(get("/events"))
                      .andExpect(request().asyncStarted())
                      .andExpect(status().isOk())
                      .andReturn();
              mvcResult.getAsyncResult(5000L); // walkaround
              mockMvc.perform(asyncDispatch(mvcResult))
                      .andExpect(content().string("data:event0\n\ndata:event1\n\ndata:event2\n\n"));
          }
      }
      
      

      Spring Boot version used is 2.0.2.RELEASE

      Seems like default getAsyncResult(-1) instead of waiting forever doesn't wait at all. As a walkaround you can add mvcResult.getAsyncResult(5000L) and then perform asyncDispatch on the mvcResult

       

        Attachments

          Activity

            People

            • Assignee:
              rstoya05-aop Rossen Stoyanchev
              Reporter:
              adik993 Adrian S
              Last updater:
              Stéphane Nicoll
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                12 weeks ago