WebTestClient is a Spring WebFlux testing client for exercising reactive HTTP controllers and checking their responses without starting a server. Bind it directly to a controller for focused tests, or autowire it against a running Spring Boot application for integration tests. section 12.3 section 12.3.3
A typical test follows this sequence:
- Create test data with Flux, Mono, or helper methods, then mock the repository when testing only the controller. section 12.3.1 section 12.3.2
- Build a client with WebTestClient.bindToController(new TacoController(repository)).build(). section 12.3.1
- Describe a request with get(), post(), put(), patch(), delete(), or head(), provide its URI and body when needed, and call exchange(). section 12.3.2
- Assert status codes and response content with expectStatus(), jsonPath(), expectBody(), json(), or expectBodyList(). section 12.3.1 section 12.3.2
Testing a taco creation endpoint
A focused POST test sends an application/json Taco and verifies that the controller returns HTTP 201 with the saved Taco: testClient.post().uri("/api/tacos").contentType(MediaType.APPLICATION_JSON).body(unsavedTacoMono, Taco.class).exchange().expectStatus().isCreated().expectBody(Taco.class).isEqualTo(savedTaco). section 12.3.2
Listing 12.1 Using WebTestClient to test TacoController
package tacos.web.api;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import tacos.Ingredient;
import tacos.Ingredient.Type;
import tacos.Taco;
import tacos.data.TacoRepository;
public class TacoControllerTest {
@Test
public void shouldReturnRecentTacos() {
Taco[] tacos = {
testTaco(1L), testTaco(2L),
testTaco(3L), testTaco(4L), #1
testTaco(5L), testTaco(6L),
testTaco(7L), testTaco(8L),
testTaco(9L), testTaco(10L),
testTaco(11L), testTaco(12L),
testTaco(13L), testTaco(14L),
testTaco(15L), testTaco(16L)};
Flux<Taco> tacoFlux = Flux.just(tacos);
TacoRepository tacoRepo = Mockito.mock(TacoRepository.class);
when(tacoRepo.findAll()).thenReturn(tacoFlux); #2
WebTestClient testClient = WebTestClient.bindToController(
new TacoController(tacoRepo))
.build(); #3
testClient.get().uri("/api/tacos?recent")
.exchange() #4
.expectStatus().isOk() #5
.expectBody()
.jsonPath("$").isArray()
.jsonPath("$").isNotEmpty()
.jsonPath("$[0].id").isEqualTo(tacos[0].getId().toString())
.jsonPath("$[0].name").isEqualTo("Taco 1")
.jsonPath("$[1].id").isEqualTo(tacos[1].getId().toString())
.jsonPath("$[1].name").isEqualTo("Taco 2")
.jsonPath("$[11].id").isEqualTo(tacos[11].getId().toString())
.jsonPath("$[11].name").isEqualTo("Taco 12")
.jsonPath("$[12]").doesNotExist();
}
...
}
Use the complete controller test listing to see mock repository setup, generated Taco fixtures, client binding, request execution, and response assertions together. section 12.3.1
package tacos;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class TacoControllerWebTest {
@Autowired
private WebTestClient testClient;
}
Use the integration-test listing to see Spring Boot RANDOM_PORT setup and WebTestClient autowiring for requests against a live server. section 12.3.3
