You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用WebTestClient对JSON接口响应进行软断言(Soft Assert)?

如何使用WebTestClient对JSON接口响应进行软断言(Soft Assert)?

你遇到的问题核心原因其实很简单:WebTestClient的响应体是一次性资源,每次调用expectBody()都会读取并消费掉响应体。你原来用expectAll的写法里,第一个lambda中的r.expectBody()已经把响应体读完了,第二个lambda再调用expectBody()时,响应体已经是空的,所以才会抛出json can not be null or empty的错误。

要实现真正的软断言(即所有断言都执行完毕后再统一报告错误),我们可以换一种思路:先把响应体完整提取出来,再在提取后的内容上做批量断言。下面给你两种常用的解决方案:

方案一:提取响应体字符串,用AssertJ软断言配合JsonPath检查

AssertJ是Spring Boot测试依赖中默认包含的断言库,它的软断言可以完美满足你的需求,所有断言都会执行,最后一次性抛出所有失败的断言信息。

import org.assertj.core.api.SoftAssertions;
import com.jayway.jsonpath.JsonPath;

@Test
public void testAssertJson() {
    // 先获取完整的响应体字符串(注意:blockFirst()适用于单响应场景)
    String responseBody = webClient.get().uri("/some-endpoint")
            .exchange()
            .expectStatus().isOk()
            .returnResult(String.class)
            .getResponseBody()
            .blockFirst();

    // 使用AssertJ软断言执行所有检查
    try (SoftAssertions softly = new SoftAssertions()) {
        // 检查第一个子分类的code和libelle
        softly.assertThat(JsonPath.read(responseBody, "$.subCategories[0].code"))
                .isEqualTo("1");
        softly.assertThat(JsonPath.read(responseBody, "$.subCategories[0].libelle"))
                .isEqualTo("foo");
        
        // 可以继续添加更多断言,比如第二个子分类
        softly.assertThat(JsonPath.read(responseBody, "$.subCategories[1].code"))
                .isEqualTo("2");
        softly.assertThat(JsonPath.read(responseBody, "$.subCategories[1].libelle"))
                .isEqualTo("bar");
        
        // 最后触发断言检查,会抛出所有失败的断言信息
        softly.assertAll();
    }
}

方案二:将响应体转换为POJO对象,直接断言对象属性

如果你的接口响应有对应的Java实体类,这种方式会更直观,避免写JsonPath表达式:

首先定义对应响应结构的实体类:

import lombok.Data;
import java.util.List;

@Data
class ApiResponse {
    private List<SubCategory> subCategories;
}

@Data
class SubCategory {
    private String code;
    private String libelle;
}

然后编写测试代码:

import org.assertj.core.api.SoftAssertions;

@Test
public void testAssertJson() {
    // 将响应体直接转换为POJO对象
    ApiResponse response = webClient.get().uri("/some-endpoint")
            .exchange()
            .expectStatus().isOk()
            .expectBody(ApiResponse.class)
            .returnResult()
            .getResponseBody();

    // 用软断言检查对象属性
    try (SoftAssertions softly = new SoftAssertions()) {
        softly.assertThat(response.getSubCategories()).hasSize(2);
        
        SubCategory firstCategory = response.getSubCategories().get(0);
        softly.assertThat(firstCategory.getCode()).isEqualTo("1");
        softly.assertThat(firstCategory.getLibelle()).isEqualTo("foo");
        
        SubCategory secondCategory = response.getSubCategories().get(1);
        softly.assertThat(secondCategory.getCode()).isEqualTo("2");
        softly.assertThat(secondCategory.getLibelle()).isEqualTo("bar");
        
        softly.assertAll();
    }
}

为什么原来的写法不行?

再强调下:expectAll的设计是用来收集多个不共享资源的断言错误,比如同时断言响应状态码、响应头、响应体,但如果多个断言都要读取响应体,就会出现资源被提前消费的问题。你原来的每个lambda都调用r.expectBody(),相当于每次都要重新读取响应体,而响应体只能被读取一次,所以第二个断言必然失败。

备注:内容来源于stack exchange,提问作者jmaniquet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 10:43:00