# 3.7WebTestClient

WebTestClient是围绕WebClient的一个瘦shell，使用它来执行请求并公开一个专用的，流畅的API来验证响应。 WebTestClient通过使用模拟请求和响应绑定到WebFlux应用程序，或者它可以通过HTTP连接测试任何Web服务器。

## 3.7.1 Setup

要创建WebTestClient，你必须选择多个服务器设置选项之一。 实际上，你要么将WebFlux应用程序配置为绑定到，要么使用URL连接到正在运行的服务器。

**绑定到控制器**

以下示例显示如何创建服务器设置以一次测试一个@Controller：

```
    ​client = WebTestClient.bindToController(new TestController()).build();
```

上面的示例加载WebFlux Java配置并注册给定的控制器。 通过使用模拟请求和响应对象，在没有HTTP服务器的情况下测试生成的WebFlux应用程序。 构建器上有更多方法可以自定义默认的WebFlux Java配置。

**绑定到路由器功能**

以下示例显示如何从RouterFunction设置服务器：

```java
​RouterFunction<?> route = ...
    client = WebTestClient.bindToRouterFunction(route).build();
```

在内部，配置传递给RouterFunctions.toWebHandler。 通过使用模拟请求和响应对象，在没有HTTP服务器的情况下测试生成的WebFlux应用程序。

**绑定到ApplicationContext**

以下示例说明如何从应用程序的Spring配置或其某个子集设置服务器：

```java
    ​@RunWith(SpringRunner.class)
    @ContextConfiguration(classes = WebConfig.class)￼
    public class MyTests {

        @Autowired
        private ApplicationContext context;￼

        private WebTestClient client;

        @Before
        public void setUp() {
            client = WebTestClient.bindToApplicationContext(context).build();￼
        }
```

在内部，配置被传递给WebHttpHandlerBuilder以设置请求处理链。 有关更多详细信息，请参阅WebHandler API。 通过使用模拟请求和响应对象，在没有HTTP服务器的情况下测试生成的WebFlux应用程序。

**绑定到服务器**

以下服务器设置选项允许你连接到正在运行的服务器：

```
    ​client = WebTestClient.bindToServer().baseUrl("http://localhost:8080").build();
```

**Client Builder**

除了前面描述的服务器设置选项之外，你还可以配置客户端选项，包括基本URL，默认标头，客户端过滤器等。 bindToServer之后可以使用这些选项。 对于所有其他人，你需要使用configureClient（）从服务器配置转换为客户端配置，如下所示：

```java
client = WebTestClient.bindToController(new TestController())
            .configureClient()
            .baseUrl("/test")
            .build();
```

## 3.7.2 写Tests

WebTestClient提供与WebClient相同的API，直到使用exchange（）执行请求。 在exchange（）之后是用于验证响应的链式API工作流。

通常，你首先声明响应状态和标头，如下所示：

```java
   client.get().uri("/persons/1")
            .accept(MediaType.APPLICATION_JSON_UTF8)
            .exchange()
            .expectStatus().isOk()
            .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
            // ...
```

然后指定如何解码和使用响应正文：

* expectBody（Class \<T>）：解码为单个对象。
* expectBodyList（Class \<T>）：解码并收集List \<T>的对象。
* expectBody（）：为JSON内容或空体解码为byte \[]。

然后你可以为身体使用内置断言。 以下示例显示了一种方法：

```java
​client.get().uri("/persons")
            .exchange()
            .expectStatus().isOk()
            .expectBodyList(Person.class).hasSize(3).contains(person)
```

你还可以超越内置断言并创建自己的断言，如以下示例所示：

```java
 client.get().uri("/persons/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody(Person.class)
            .consumeWith(result -> {
                // custom assertions (e.g. AssertJ)...
            });
```

你也可以退出工作流程并获得结果，如下所示：

```java
   EntityExchangeResult<Person> result = client.get().uri("/persons/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody(Person.class)
            .returnResult();
```

> 当你需要使用泛型解码为目标类型时，请查找接受ParameterizedTypeReference而不是Class \<T>的重载方法。

**无内容**

如果响应没有内容（或者你不关心它），请使用Void.class，以确保释放资源。 以下示例显示了如何执行此操作：

```java
 ​client.get().uri("/persons/123")
            .exchange()
            .expectStatus().isNotFound()
            .expectBody(Void.class);
```

或者，如果要断言没有响应内容，可以使用类似于以下内容的代码：

```java
client.post().uri("/persons")
            .body(personMono, Person.class)
            .exchange()
            .expectStatus().isCreated()
            .expectBody().isEmpty();
```

**JSON Content**

使用expectBody（）时，响应将作为byte \[]使用。 这对原始内容断言很有用。 例如，你可以使用JSONAssert来验证JSON内容，如下所示：

```java
client.get().uri("/persons/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody()
            .json("{\"name\":\"Jane\"}")
```

你还可以使用JSONPath表达式，如下所示：

```java
​client.get().uri("/persons")
            .exchange()
            .expectStatus().isOk()
            .expectBody()
            .jsonPath("$[0].name").isEqualTo("Jane")
            .jsonPath("$[1].name").isEqualTo("Jason");
```

**Streaming Responses**

要测试无限流（例如，“text / event-stream”或“application / stream + json”），你需要在响应状态和标头断言之后立即退出链式API（通过使用returnResult），如下所示 示例显示：

```java
    ​FluxExchangeResult<MyEvent> result = client.get().uri("/events")
            .accept(TEXT_EVENT_STREAM)
            .exchange()
            .expectStatus().isOk()
            .returnResult(MyEvent.class);
```

现在你可以使用Flux \<T>，在它们到来时断言已解码的对象，然后在满足测试目标时取消。 我们建议使用reactor-test模块中的StepVerifier来执行此操作，如以下示例所示：

```java
    ​Flux<Event> eventFux = result.getResponseBody();

    StepVerifier.create(eventFlux)
            .expectNext(person)
            .expectNextCount(4)
            .consumeNextWith(p -> ...)
            .thenCancel()
            .verify();
```

**Request Body**

在构建请求时，WebTestClient提供了与WebClient相同的API，实现主要是简单的传递。 有关如何使用正文准备请求的示例，请参阅WebClient文档，包括提交表单数据，多部分请求等。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flydean.com/spring-framework-documentation5/testing/integration-testing/3.7webtestclient.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
