Refactor WebTestClient assertions

Rather than returning ExchangeActions that contains ExchangeInfo and
applies a chain of assertions, the exchange operation in WebTestClient
now has an extra step to decode the response.

The outcome of that is ExchangeResult<T>, parameterized with the
decoded body type, and containing the request and response details,
also providing access to built-inassertions via an assertThat()
instance method.

This approach lends itself better to decoding and asserting response
body types with generecis. It is also more friendly to using any
assertion library such as AssertJ since you get the result first
and then deal with assertions.
This commit is contained in:
Rossen Stoyanchev
2017-02-15 18:10:57 -05:00
parent 50d93d3794
commit 51f2042e97
24 changed files with 635 additions and 1548 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.test.web.reactive.server.samples;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -43,14 +44,18 @@ public class ErrorTests {
public void notFound() throws Exception {
this.client.get().uri("/invalid")
.exchange()
.assertStatus().isNotFound();
.expectNoBody()
.assertThat()
.status().isNotFound();
}
@Test
public void serverException() throws Exception {
this.client.get().uri("/server-error")
.exchange()
.assertStatus().isInternalServerError();
.expectNoBody()
.assertThat()
.status().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
}

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.test.web.reactive.server.samples;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
@@ -27,8 +25,6 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.junit.Assert.assertEquals;
/**
* Tests with custom headers.
*
@@ -53,17 +49,20 @@ public class HeaderTests {
public void requestResponseHeaderPair() throws Exception {
this.client.get().uri("/request-response-pair").header("h1", "in")
.exchange()
.assertStatus().isOk()
.assertHeader("h1").isEqualTo("in-out");
.expectNoBody()
.assertThat()
.status().isOk()
.header().valueEquals("h1", "in-out");
}
@Test
public void headerConsumer() throws Exception {
public void headerMultivalue() throws Exception {
this.client.get().uri("/multivalue")
.exchange()
.assertStatus().isOk()
.assertHeader("h1").consume(value -> assertEquals("v1", value))
.assertHeader("h1").values().consume(values -> assertEquals(Arrays.asList("v1", "v2", "v3"), values));
.expectNoBody()
.assertThat()
.status().isOk()
.header().valueEquals("h1", "v1", "v2", "v3");
}

View File

@@ -16,6 +16,7 @@
package org.springframework.test.web.reactive.server.samples;
import java.net.URI;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -25,9 +26,12 @@ import org.junit.Before;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.web.reactive.server.ExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -37,7 +41,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.springframework.http.MediaType.TEXT_EVENT_STREAM;
@@ -62,37 +65,54 @@ public class ResponseEntityTests {
public void entity() throws Exception {
this.client.get().uri("/persons/John")
.exchange()
.assertStatus().isOk()
.assertHeaders().contentType(MediaType.APPLICATION_JSON_UTF8)
.assertEntity(Person.class).isEqualTo(new Person("John"));
.decodeEntity(Person.class)
.assertThat()
.status().isOk()
.header().contentTypeEquals(MediaType.APPLICATION_JSON_UTF8)
.bodyEquals(new Person("John"));
}
@Test
public void entityList() throws Exception {
this.client.get().uri("/persons")
.exchange()
.assertStatus().isOk()
.assertHeaders().contentType(MediaType.APPLICATION_JSON_UTF8)
.assertEntity(Person.class).list()
.hasSize(3)
.contains(new Person("Jane"), new Person("Jason"), new Person("John"));
.decodeAndCollect(Person.class)
.assertThat()
.status().isOk()
.header().contentTypeEquals(MediaType.APPLICATION_JSON_UTF8)
.bodyEquals(Arrays.asList(new Person("Jane"), new Person("Jason"), new Person("John")));
}
@Test
public void entityMap() throws Exception {
Map<String, Person> map = new LinkedHashMap<>();
map.put("Jane", new Person("Jane"));
map.put("Jason", new Person("Jason"));
map.put("John", new Person("John"));
this.client.get().uri("/persons?map=true")
.exchange()
.assertStatus().isOk()
.assertEntity(Person.class).map().hasSize(3).containsKeys("Jane", "Jason", "John");
.decodeEntity(ResolvableType.forClassWithGenerics(Map.class, String.class, Person.class))
.assertThat()
.status().isOk()
.bodyEquals(map);
}
@Test
public void entityStream() throws Exception {
this.client.get().uri("/persons").accept(TEXT_EVENT_STREAM)
ExchangeResult<Flux<Person>> result = this.client.get()
.uri("/persons")
.accept(TEXT_EVENT_STREAM)
.exchange()
.assertStatus().isOk()
.assertHeaders().contentType(TEXT_EVENT_STREAM)
.assertEntity(Person.class).stepVerifier()
.decodeFlux(Person.class);
result.assertThat()
.status().isOk()
.header().contentTypeEquals(TEXT_EVENT_STREAM);
StepVerifier.create(result.getResponseBody())
.expectNext(new Person("N0"), new Person("N1"), new Person("N2"))
.expectNextCount(4)
.consumeNextWith(person -> assertThat(person.getName(), endsWith("7")))
@@ -104,17 +124,10 @@ public class ResponseEntityTests {
public void postEntity() throws Exception {
this.client.post().uri("/persons")
.exchange(Mono.just(new Person("John")), Person.class)
.assertStatus().isCreated()
.assertHeader("location").isEqualTo("/persons/John").and()
.assertBody().isEmpty();
}
@Test
public void entityConsumer() throws Exception {
this.client.get().uri("/persons/John")
.exchange()
.assertStatus().isOk()
.assertEntity(Person.class).consume(p -> assertEquals(new Person("John"), p));
.expectNoBody()
.assertThat()
.status().isCreated()
.header().valueEquals("location", "/persons/John");
}

View File

@@ -51,8 +51,10 @@ public class ApplicationContextTests {
public void test() throws Exception {
this.client.get().uri("/test")
.exchange()
.assertStatus().isOk()
.assertEntity(String .class).isEqualTo("It works!");
.decodeEntity(String.class)
.assertThat()
.status().isOk()
.bodyEquals("It works!");
}

View File

@@ -43,8 +43,10 @@ public class ControllerTests {
public void test() throws Exception {
this.client.get().uri("/test")
.exchange()
.assertStatus().isOk()
.assertEntity(String .class).isEqualTo("It works!");
.decodeEntity(String.class)
.assertThat()
.status().isOk()
.bodyEquals("It works!");
}

View File

@@ -68,8 +68,10 @@ public class HttpServerTests {
public void test() throws Exception {
this.client.get().uri("/test")
.exchange()
.assertStatus().isOk()
.assertEntity(String .class).isEqualTo("It works!");
.decodeEntity(String.class)
.assertThat()
.status().isOk()
.bodyEquals("It works!");
}
}

View File

@@ -49,8 +49,10 @@ public class RouterFunctionTests {
public void test() throws Exception {
this.testClient.get().uri("/test")
.exchange()
.assertStatus().isOk()
.assertEntity(String .class).isEqualTo("It works!");
.decodeEntity(String.class)
.assertThat()
.status().isOk()
.bodyEquals("It works!");
}
}