REST-assured 使用指南

更新于 2025-12-30

baeldung 2025-01-11

1. 简介

REST-assured 的设计目标是简化 REST API 的测试与验证,其灵感主要来源于 Ruby 和 Groovy 等动态语言中常用的测试技术。

该库对 HTTP 协议提供了强大的支持,不仅涵盖标准的 HTTP 方法(如 GET、POST 等),还支持更高级的功能。

在本指南中,我们将深入探索 REST-assured,并使用 Hamcrest 进行断言。如果你还不熟悉 Hamcrest,建议先阅读官方入门文档:Hamcrest 入门教程

现在,让我们从一个简单的示例开始。


2. 简单示例测试

在开始之前,请确保你的测试类中包含以下静态导入:

import static io.restassured.RestAssured.*;
import static io.restassured.matcher.RestAssuredMatchers.*;
import static org.hamcrest.Matchers.*;

这些导入将使测试代码更加简洁,并能轻松访问 REST-assured 的核心 API。

假设我们有一个简单的投注系统,它通过 API 暴露如下游戏数据:

{
    "id": "390",
    "data": {
        "leagueId": 35,
        "homeTeam": "Norway",
        "visitingTeam": "England"
    },
    "odds": [
        {
            "price": "1.30",
            "name": "1"
        },
        {
            "price": "5.25",
            "name": "X"
        }
    ]
}

该 JSON 响应来自本地部署的 API:http://localhost:8080/events?id=390

现在,我们使用 REST-assured 来验证响应中的某些关键字段:

@Test
public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() {
   get("/events?id=390").then().statusCode(200).assertThat()
      .body("data.leagueId", equalTo(35)); 
}

这段代码验证了:调用 /events?id=390 接口后,返回的 JSON 响应中 data.leagueId 的值为 35。

再看一个更复杂的例子:验证 odds 数组中包含价格 "1.30""5.25"

@Test
public void givenUrl_whenJsonResponseHasArrayWithGivenValuesUnderKey_thenCorrect() {
   get("/events?id=390").then().assertThat()
      .body("odds.price", hasItems("1.30", "5.25"));
}

3. REST-assured 环境配置

如果你使用 Maven,请在 pom.xml 中添加以下依赖:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>rest-assured</artifactId>
    <version>5.5.0</version>
    <scope>test</scope>
</dependency>

由于 REST-assured 依赖 Hamcrest 匹配器进行断言,因此还需添加 Hamcrest 依赖:

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest</artifactId>
    <version>2.1</version>
</dependency>

4. 匿名 JSON 根节点验证

考虑如下仅包含基本类型元素的 JSON 数组:

[1, 2, 3]

这种结构被称为 匿名 JSON 根节点(anonymous JSON root)——它没有键值对,但仍是合法的 JSON。

我们可以使用 $ 符号或空字符串 "" 作为路径来验证此类响应。假设该服务通过 http://localhost:8080/json 提供:

when().get("/json").then().body("$", hasItems(1, 2, 3));

或者:

when().get("/json").then().body("", hasItems(1, 2, 3));

两者效果相同。


5. 浮点数(Float 与 Double)

使用 REST-assured 测试 REST 服务时需注意:JSON 响应中的浮点数会被映射为 Java 的 float 类型,而非 double。

例如,有如下 JSON:

{
    "odd": {
        "price": "1.30",
        "ck": 12.2,
        "name": "1"
    }
}

如果执行以下测试:

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));

该测试会失败,因为 12.2 在 Java 中默认是 double 类型,而 JSON 中的 12.2 被解析为 float

正确写法是显式指定为 float:

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));

6. 指定请求方法

通常我们会直接调用 get()post() 等方法来发起对应 HTTP 请求。

此外,也可以使用 request() 方法显式指定 HTTP 动词:

@Test
public void whenRequestGet_thenOK(){
    when().request("GET", "/users/eugenp").then().statusCode(200);
}

这等价于直接调用 get("/users/eugenp")

同样支持 HEADCONNECTOPTIONS 等方法:

@Test
public void whenRequestHead_thenOK() {
    when().request("HEAD", "/users/eugenp").then().statusCode(200);
}

对于 POST 请求,可通过 with().body() 设置请求体:

@Test
public void whenRequestedPost_thenCreated() {
    with().body(new Odd(5.25f, 1, 13.1f, "X"))
      .when()
      .request("POST", "/odds/new")
      .then()
      .statusCode(201);
}

其中 Odd 对象会自动序列化为 JSON。你也可以直接传入字符串作为请求体。


7. 默认值配置

我们可以在测试前统一配置默认参数:

@BeforeEach
public void setup() {
    RestAssured.baseURI = "https://api.github.com";
    RestAssured.port = 443;
}

除了 baseURIport,还可配置 basePathrootPath 以及认证信息。

若要重置为 REST-assured 的默认设置,可调用:

RestAssured.reset();

8. 测量响应时间

可通过 Response 对象的 time()timeIn() 方法测量响应耗时:

@Test
public void whenMeasureResponseTime_thenOK() {
    Response response = RestAssured.get("/users/eugenp");
    long timeInMS = response.time(); // 毫秒
    long timeInS = response.timeIn(TimeUnit.SECONDS); // 秒
    
    assertEquals(timeInS, timeInMS / 1000);
}

8.1 验证响应时间

还可以直接在断言中验证响应时间(单位:毫秒):

@Test
public void whenValidateResponseTime_thenSuccess() {
    when().get("/users/eugenp").then().time(lessThan(5000L));
}

若要使用其他时间单位,可传入 TimeUnit 参数:

@Test
public void whenValidateResponseTimeInSeconds_thenSuccess(){
    when().get("/users/eugenp").then().time(lessThan(5L), TimeUnit.SECONDS);
}

9. XML 响应验证

REST-assured 不仅支持 JSON,也支持 XML 响应验证。

假设请求 http://localhost:8080/employees 返回如下 XML:

<employees>
    <employee category="skilled">
        <first-name>Jane</first-name>
        <last-name>Daisy</last-name>
        <sex>f</sex>
    </employee>
</employees>

验证 first-name 是否为 "Jane":

@Test
public void givenUrl_whenXmlResponseValueTestsEqual_thenCorrect() {
    post("/employees").then().assertThat()
      .body("employees.employee.first-name", equalTo("Jane"));
}

也可链式验证多个字段:

@Test
public void givenUrl_whenMultipleXmlValuesTestEqual_thenCorrect() {
    post("/employees").then().assertThat()
      .body("employees.employee.first-name", equalTo("Jane"))
      .body("employees.employee.last-name", equalTo("Daisy"))
      .body("employees.employee.sex", equalTo("f"));
}

或使用简写形式(变长参数):

@Test
public void givenUrl_whenMultipleXmlValuesTestEqualInShortHand_thenCorrect() {
    post("/employees")
      .then().assertThat()
      .body(
        "employees.employee.first-name", equalTo("Jane"),
        "employees.employee.last-name", equalTo("Daisy"),
        "employees.employee.sex", equalTo("f")
      );
}

10. 使用 XPath 验证 XML

REST-assured 支持通过 XPath 表达式验证 XML:

@Test
public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() {
    post("/employees").then().assertThat()
      .body(hasXPath("/employees/employee/first-name", containsString("Ja")));
}

也可以直接在 XPath 中使用文本匹配:

@Test
public void givenUrl_whenValidatesXmlUsingXpath2_thenCorrect() {
    post("/employees").then().assertThat()
      .body(hasXPath("/employees/employee/first-name[text()='Jane']"));
}

11. 日志记录测试详情

11.1 记录请求详情

使用 log().all() 可记录完整请求信息:

@Test
public void whenLogRequest_thenOK() {
    given().log().all()
      .when().get("/users/eugenp")
      .then().statusCode(200);
}

输出示例:

Request method:	GET
Request URI:	https://api.github.com:443/users/eugenp
Proxy:			<none>
Request params:	<none>
Query params:	<none>
Form params:	<none>
Path params:	<none>
Multiparts:		<none>
Headers:		Accept=*/*
Cookies:		<none>
Body:			<none>

也可只记录特定部分,如 params()body()headers()cookies() 等:

@Test
public void whenLogRequestParams_thenOK() { 
    given().log().params()
      .when().get("/users/eugenp")
      .then().statusCode(200);
}

注意:其他库或过滤器可能会修改实际发送的内容,因此日志仅反映初始请求规范。

11.2 记录响应详情

记录响应体:

@Test
public void whenLogResponse_thenOK() {
    when().get("/repos/eugenp/tutorials")
      .then().log().body().statusCode(200);
}

输出示例(截取部分):

{
    "id": 9754983,
    "name": "tutorials",
    "full_name": "eugenp/tutorials",
    "private": false,
    "html_url": "https://github.com/eugenp/tutorials",
    "description": "The \"REST With Spring\" Course: ",
    "fork": false,
    "size": 72371,
    "license": {
        "key": "mit",
        "name": "MIT License",
        "spdx_id": "MIT",
        "url": "https://api.github.com/licenses/mit"
    },
    ...
}

11.3 条件性记录响应

仅在发生错误或状态码匹配时记录响应:

@Test
public void whenLogResponseIfErrorOccurred_thenSuccess() {
    when().get("/users/eugenp")
      .then().log().ifError();
      
    when().get("/users/eugenp")
      .then().log().ifStatusCodeIsEqualTo(500);
      
    when().get("/users/eugenp")
      .then().log().ifStatusCodeMatches(greaterThan(200));
}

11.4 验证失败时记录请求与响应

仅当断言失败时才记录:

@Test
public void whenLogOnlyIfValidationFailed_thenSuccess() {
    when().get("/users/eugenp")
      .then().log().ifValidationFails().statusCode(200);

    given().log().ifValidationFails()
      .when().get("/users/eugenp")
      .then().statusCode(200);
}

12. 结论

在本教程中,我们全面介绍了 REST-assured 框架,并展示了其用于测试 RESTful 服务和验证响应的核心功能。无论是 JSON 还是 XML,同步还是异步,简单断言还是复杂条件日志,REST-assured 都提供了简洁而强大的 API,极大提升了 API 自动化测试的效率与可读性。