Skip to content

Using MockMvc to test Flux<ServerSentEvent> endpoints? #22544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kdvolder opened this issue Mar 7, 2019 · 9 comments
Closed

Using MockMvc to test Flux<ServerSentEvent> endpoints? #22544

kdvolder opened this issue Mar 7, 2019 · 9 comments
Assignees
Labels
type: documentation A documentation task
Milestone

Comments

@kdvolder
Copy link
Member

kdvolder commented Mar 7, 2019

I'm strugling with writing tests for rest endpoints that return Flux. I'd like to use mockMvc for this. Is this even possible?

Note that, allthough I have some rest endpoints returning Flux and/or Mono I am not using WebFllux but just regular MVC. The framework handles those types fine and for my Flux example, these are received on the browser side as a 'stream' of ServerSentEvents. This all works fine.

The trouble is. I can't seem to find a good way to write tests agains these endpoints using mockmvc.

The best I came up with is here:

https://github.com/kdvolder/mvc-flux-testing/blob/master/src/test/java/com/example/demo/MvcFluxDemoApplicationTests.java

Is turning the stream of events into String via `getResponse().getContentAsString()' really the best I can do here? (Note: @rstoyanchev has told me it is, mockMvc doesn't provide good ways to test this sort of thing. He recommends to use WebTestClient).

So I submit for consideration the following:

  1. It may be useful to expand mockMvc apis to handle endpoints returning streamed ServerSentEvents better. Perhaps, something that can get the content as a Flux? Then we can use reactive testing support such as StepVerifier to validate the content in our tests.

  2. Add a pointer in the docs on using WebTestClient instead for these scenarios.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Mar 7, 2019
@kdvolder
Copy link
Member Author

kdvolder commented Mar 7, 2019

Note: I don't seem to be the only one strugling with this. See: https://stackoverflow.com/questions/35499655/how-to-test-server-sent-events-with-spring

There are two answers there, neither of which seem very satisfactory.

@rstoyanchev
Copy link
Contributor

Thanks Kris. Indeed WebTestClient is the best answer we have for this. Building something (async, streaming parsing) in MockMvc would be non-trivial. We certainly can and need to explain the available options in the documentation however.

@rstoyanchev rstoyanchev added this to the 5.1.6 milestone Mar 9, 2019
@rstoyanchev rstoyanchev added type: documentation A documentation task and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Mar 9, 2019
@rstoyanchev rstoyanchev self-assigned this Mar 11, 2019
@daliborfilus
Copy link

How can we use WebTestClient with Spring Boot MVC (starter-web)?
It throws unsatisfied dependency on me. WebTestClient is in reactive namespaced package.
So we cannot use it for regular MVC?
(I tried @AutoConfigureWebTestClient, but that didn't help; even the doc for that annotations says it's only for WebFlux.)

So if WebTestClient is for WebFlux only, and MockMvc is for MVC only, and we want to test Flux/Mono responses, what should we do? I'm trying to find an answer to this for multiple hours at this point... and each of such answers is contradictory or confusing or incorrect, because things have changed.

@rstoyanchev
Copy link
Contributor

@daliborfilus, indeed WebTestClient uses WebClient from spring-webflux internally but this is a fully expected supported scenario in Spring Boot, just like it is fully supported to use the WebClient in a Spring Boot application with WebMvc. These are the rules Boot uses to determine the web environment.

In addition in 5.3 we updated WebTestClient to support both WebFlux and WebMvc controllers so it can be used for both. You might want to review the updated reference in the WebTestClient and the MockMvc sections.

That said, for testing SSE and streaming responses, full end-to-end tests through the WebTestClient along with the StepVerifier from Project Reactor is your best bet.

@daliborfilus
Copy link

daliborfilus commented Mar 1, 2021

@rstoyanchev Thank you. Seem like I tried to use WebTestClient as autowired component, which doesn't work in this case?
(I'm using Spring Boot 2.4.3, which uses Spring Web 5.3.4.)

The WebTestClient link you mention links to sample project https://github.com/spring-projects/spring-framework/tree/master/spring-test/src/test/java/org/springframework/test/web/client/samples which doesn't use WebTestClient, at least not directly? One of the tests mocks the server side completely, which I don't want - I just want to mock the servlet part and still do end-to-end integration test. (Or even expose random port and test it using rest template - which is maybe what this sample project does? But that's another topic.)

The part 3.6.1 - Setup explains how to create instance of WebTestClient - or more precisely MockMvcWebTestClient - bound to a controller or the application context. I don't want to instantiate the controller here, because then I would need to pass all autowired dependencies to it, so the ApplicationContext is preferrable I think.

So following this, I now have this code:

@ActiveProfiles("test")
// tried with RANDOM_PORT and MOCK
@SpringBootTest(classes = [BackendApplication::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc(printOnlyOnFailure = false)
@AutoConfigureRestDocs(outputDir = "build/generated-snippets")
internal class MonitoringControllerTest {

    @Autowired
    lateinit var context: WebApplicationContext

    lateinit var client: WebTestClient

    @BeforeEach
    fun setUp() {
        client = WebTestClient.bindToApplicationContext(context).build()
    }

    @Test
    fun indexAcceptJsonAndDocument() {
        client.get().uri("$contextPath/entities/export")
//                .with(SecurityMockMvcRequestPostProcessors.user("user").authorities(allAuthorities())) // FIXME: find replacement for this
            .accept(MediaType.APPLICATION_JSON)
            .exchange()
            .expectStatus().isOk
            .expectBody()
            .jsonPath("$.totalElements").isEqualTo(3)
            .jsonPath("$.totalPages").isEqualTo(1)
//            .andDo(MockMvcRestDocumentation.document("monitored-entities-export")) // FIXME: find replacement for this to get restdocs working; use consumeWith?
    }
}

which throws and I don't know why:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'webHandler' available
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:863)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1344)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:309)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:213)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1159)
	at org.springframework.web.server.adapter.WebHttpHandlerBuilder.applicationContext(WebHttpHandlerBuilder.java:165)
	at org.springframework.test.web.reactive.server.ApplicationContextSpec.initHttpHandlerBuilder(ApplicationContextSpec.java:43)
	at org.springframework.test.web.reactive.server.AbstractMockServerSpec.configureClient(AbstractMockServerSpec.java:86)
	at org.springframework.test.web.reactive.server.AbstractMockServerSpec.build(AbstractMockServerSpec.java:107)
	at app.web.data.monitoring.MonitoringControllerTest.setUp(MonitoringControllerTest.kt:35)

Some relevant links: https://stackoverflow.com/questions/60792015/springboot-test-fails-with-no-bean-named-webhandler-available

Tried adding @AutoConfigureWebTestClient - didn't help.
Tried adding dependency on starter-webflux along with AutoConfigureWebTestClient - didn't help.

@daliborfilus
Copy link

@rstoyanchev Uh. I tried to at least solve the todos in the comments above (mock user, apply rest docs) and I found out that I cannot use .apply(springSecurity()) in the test because of #20606 - kotlin cannot infer the type T.
I would like to supply the the type to the apply method, but I cannot, because the type is package-private - and I can see you are the author of that class (spring-test-5.3.4-sources.jar!/org/springframework/test/web/reactive/server/ApplicationContextSpec.java).

So even if I could get that "webHandler" dependency error go away, I still cannot use the WebTestClient in Kotlin.

@rstoyanchev
Copy link
Contributor

As you already found out, you need to use MockMvcWebTestClient. That has a method to bind to an ApplicationContext as well so change to the following:

MockMvcWebTestClient.bindToApplicationContext(this.wac).build();

As for #20606 yes that is an issue. I believe there is some effort to resolve it but I don't have the details.

@daliborfilus
Copy link

@rstoyanchev Thank you, I will stick to MVC + MockMvc for now and we'll see in the future.

@rstoyanchev
Copy link
Contributor

No problem. For streaming and Server-Sent Events, you'll need the WebTestClient which can also do full end-to-end integration tests but apart from that you can also stick to MockMvc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation task
Projects
None yet
Development

No branches or pull requests

4 participants