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")。
同样支持 HEAD、CONNECT、OPTIONS 等方法:
@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;
}
除了 baseURI 和 port,还可配置 basePath、rootPath 以及认证信息。
若要重置为 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 自动化测试的效率与可读性。