提交 4982b5fc 编写于 作者: R Rossen Stoyanchev

Improve docs on SSE tests for Spring MVC

Closes gh-26687
上级 f7678cdc
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -32,6 +32,7 @@ import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
......@@ -99,7 +100,7 @@ public class AsyncTests {
}
@Test
public void deferredResultWithImmediateValue() throws Exception {
public void deferredResultWithImmediateValue() {
this.testClient.get()
.uri("/1?deferredResultWithImmediateValue=true")
.exchange()
......@@ -109,7 +110,7 @@ public class AsyncTests {
}
@Test
public void deferredResultWithDelayedError() throws Exception {
public void deferredResultWithDelayedError() {
this.testClient.get()
.uri("/1?deferredResultWithDelayedError=true")
.exchange()
......@@ -118,7 +119,7 @@ public class AsyncTests {
}
@Test
public void listenableFuture() throws Exception {
public void listenableFuture() {
this.testClient.get()
.uri("/1?listenableFuture=true")
.exchange()
......@@ -142,17 +143,17 @@ public class AsyncTests {
@RequestMapping(path = "/{id}", produces = "application/json")
private static class AsyncController {
@RequestMapping(params = "callable")
@GetMapping(params = "callable")
public Callable<Person> getCallable() {
return () -> new Person("Joe");
}
@RequestMapping(params = "streaming")
@GetMapping(params = "streaming")
public StreamingResponseBody getStreaming() {
return os -> os.write("name=Joe".getBytes(StandardCharsets.UTF_8));
}
@RequestMapping(params = "streamingSlow")
@GetMapping(params = "streamingSlow")
public StreamingResponseBody getStreamingSlow() {
return os -> {
os.write("name=Joe".getBytes());
......@@ -166,41 +167,41 @@ public class AsyncTests {
};
}
@RequestMapping(params = "streamingJson")
@GetMapping(params = "streamingJson")
public ResponseEntity<StreamingResponseBody> getStreamingJson() {
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON)
.body(os -> os.write("{\"name\":\"Joe\",\"someDouble\":0.5}".getBytes(StandardCharsets.UTF_8)));
}
@RequestMapping(params = "deferredResult")
@GetMapping(params = "deferredResult")
public DeferredResult<Person> getDeferredResult() {
DeferredResult<Person> result = new DeferredResult<>();
delay(100, () -> result.setResult(new Person("Joe")));
return result;
}
@RequestMapping(params = "deferredResultWithImmediateValue")
@GetMapping(params = "deferredResultWithImmediateValue")
public DeferredResult<Person> getDeferredResultWithImmediateValue() {
DeferredResult<Person> result = new DeferredResult<>();
result.setResult(new Person("Joe"));
return result;
}
@RequestMapping(params = "deferredResultWithDelayedError")
@GetMapping(params = "deferredResultWithDelayedError")
public DeferredResult<Person> getDeferredResultWithDelayedError() {
DeferredResult<Person> result = new DeferredResult<>();
delay(100, () -> result.setErrorResult(new RuntimeException("Delayed Error")));
return result;
}
@RequestMapping(params = "listenableFuture")
@GetMapping(params = "listenableFuture")
public ListenableFuture<Person> getListenableFuture() {
ListenableFutureTask<Person> futureTask = new ListenableFutureTask<>(() -> new Person("Joe"));
delay(100, futureTask);
return futureTask;
}
@RequestMapping(params = "completableFutureWithImmediateValue")
@GetMapping(params = "completableFutureWithImmediateValue")
public CompletableFuture<Person> getCompletableFutureWithImmediateValue() {
CompletableFuture<Person> future = new CompletableFuture<>();
future.complete(new Person("Joe"));
......
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.test.web.servlet.samples.client.standalone;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.test.web.Person;
import org.springframework.test.web.reactive.server.FluxExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import static java.time.Duration.ofMillis;
import static org.assertj.core.api.Assertions.assertThat;
/**
* SSE controller tests with MockMvc and WebTestClient.
*
* @author Rossen Stoyanchev
*/
public class SseTests {
private final WebTestClient testClient =
MockMvcWebTestClient.bindToController(new SseController()).build();
@Test
public void sse() {
FluxExchangeResult<Person> exchangeResult = this.testClient.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
}
@RestController
private static class SseController {
@GetMapping(path = "/persons", produces = "text/event-stream")
public Flux<Person> getPersonStream() {
return Flux.interval(ofMillis(100)).take(50).onBackpressureBuffer(50)
.map(index -> new Person("N" + index));
}
}
}
......@@ -7471,12 +7471,35 @@ or reactive type such as Reactor `Mono`:
[[spring-mvc-test-vs-streaming-response]]
===== Streaming Responses
There are no options built into Spring MVC Test for container-less testing of streaming
responses. However you can test streaming requests through the <<WebTestClient>>.
This is also supported in Spring Boot where you can
{doc-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server]
with `WebTestClient`. One extra advantage is the ability to use the `StepVerifier` from
project Reactor that allows declaring expectations on a stream of data.
The best way to test streaming responses such as Server-Sent Events is through the
<<WebTestClient>> which can be used as a test client to connect to a `MockMvc` instance
to perform tests on Spring MVC controllers without a running server. For example:
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
.Java
----
WebTestClient client = MockMvcWebTestClient.bindToController(new SseController()).build();
FluxExchangeResult<Person> exchangeResult = client.get()
.uri("/persons")
.exchange()
.expectStatus().isOk()
.expectHeader().contentType("text/event-stream")
.returnResult(Person.class);
// Use StepVerifier from Project Reactor to test the streaming response
StepVerifier.create(exchangeResult.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName()).endsWith("7"))
.thenCancel()
.verify();
----
`WebTestClient` can also connect to a live server and perform full end-to-end integration
tests. This is also supported in Spring Boot where you can
{doc-spring-boot}/html/spring-boot-features.html#boot-features-testing-spring-boot-applications-testing-with-running-server[test a running server].
[[spring-mvc-test-server-filters]]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册