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

See original GitHub issue

Adrian S opened SPR-16869 and commented

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


Affects: 5.0.6

Referenced from: commits https://github.com/spring-projects/spring-framework/commit/2a993bf9ff7c9a4fbb1edef8ea1e7f96ac0a1afc, https://github.com/spring-projects/spring-framework/commit/9d36fd0b68883847260863cec7131d4e77720522

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
hantsycommented, Sep 6, 2021

I have an example project written with WebMvc SseEmitter.

When switching to use MockMvcWebTestCient in tests, I still got the same exception.

org.springframework.web.reactive.function.client.WebClientRequestException: Async result for handler 
[com.example.demo.SseController#sseMessages()] was not set during the specified timeToWait=1000; nested exception is 
java.lang.IllegalStateException: Async result for handler [com.example.demo.SseController#sseMessages()] was not set during 
the specified timeToWait=1000

I have to add sseEmitter.complete in the controller to make the MockMvc and MockMvcWebTestClient work, else it will be blocked till it is timeout, and threw the exceptions.

0reactions
rstoyanchevcommented, Sep 7, 2021

The idea is you would use WebTestClient as the API for all tests but for SSE tests it would be against a live server, i.e. WebTestClient.bindToServer.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Async result for handler was not set during the specified ...
Fixed by telling the TestRunner to consider that the current test makes the context "dirty". Add @DirtiesContext(classMode = DirtiesContext.
Read more >
Testing async responses using MockMvc - Sadique Ali
This blog post describes how to write tests in such scenarios. Let's take a look at the following example. In this example, we...
Read more >
MockHttpServletRequest.getAsyncContext - Java - Tabnine
... IllegalStateException("Async result for handler [" + this.handler + "]" + " was not set during the specified timeToWait=" + timeToWait); } Object...
Read more >
Async result for handler was not set during the specified ...
Coding example for the question Async RestController endpoints :: Async result for handler was not set during the specified timeToWait=0-Spring MVC.
Read more >
org.springframework.test.web.servlet.MvcResult ...
getAsyncResult (); result= mockMvc.perform(asyncDispatch(result)) . ... getAsyncResult(0); } catch (IllegalStateException ex) { // Not set } this.printer.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found