1. 简介
REST-assured 是一个专门用于简化 REST API 测试与验证的 Java 库,其设计深受 Ruby 和 Groovy 等动态语言测试技术的影响。
该库对 HTTP 协议提供了强大支持,不仅覆盖了基础动词和标准操作,还提供了大量高级功能。
本文将深入探索 REST-assured 的核心能力,并结合 Hamcrest 进行断言验证。如果你还不熟悉 Hamcrest,建议先阅读这篇教程:Hamcrest 测试指南。
想要了解更多高级用法?推荐阅读以下文章:
现在让我们从一个简单示例开始。
2. 基础示例测试
开始前,确保你的测试类包含以下静态导入:
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*
这些导入能简化测试代码,方便访问核心 API。
下面是一个基础示例:假设我们有一个博彩系统 API,返回比赛数据:
{
"id": "390",
"data": {
"leagueId": 35,
"homeTeam": "Norway",
"visitingTeam": "England",
},
"odds": [{
"price": "1.30",
"name": "1"
},
{
"price": "5.25",
"name": "X"
}]
}
当访问 http://localhost:8080/events?id=390
时,会返回上述 JSON。现在用 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. 环境搭建
使用 Maven 时,在 pom.xml
添加 rest-assured 依赖:
<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 根节点验证
考虑一个只包含基本类型的数组:
[1, 2, 3]
这称为匿名 JSON 根节点(无键值对),但仍是合法 JSON。验证这类数据时,使用 $
或空字符串 ""
作为路径。假设接口 http://localhost:8080/json
返回上述数据:
when().get("/json").then().body("$", hasItems(1, 2, 3));
以下写法效果相同:
when().get("/json").then().body("", hasItems(1, 2, 3));
5. 浮点数处理
使用 REST-assured 时要注意:JSON 中的浮点数会被映射为 Java 的 float
类型,不能直接与 double
比较。看这个例子:
{
"odd": {
"price": "1.30",
"ck": 12.2,
"name": "1"
}
}
如果这样测试 ck
的值:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));
测试会失败,因为 equalTo(12.2)
是 double
类型。正确做法是显式指定为 float
:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));
6. 指定请求方法
通常我们直接调用 get()
、post()
等方法发送请求。但也可以通过 request()
方法指定 HTTP 动词:
@Test
public void whenRequestGet_thenOK(){
when().request("GET", "/users/eugenp").then().statusCode(200);
}
这等价于直接使用 get()
。类似地,可以发送 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;
}
这里设置了基础 URI 和端口。此外还可配置:
- 基础路径(basePath)
- 根路径(root path)
- 认证信息(authentication)
需要重置为默认值时调用:
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);
}
关键点:
-
time()
:返回毫秒级响应时间 -
timeIn()
:返回指定时间单位的响应时间
8.1. 验证响应时间
通过 long
类型的 Matcher
验证响应时间(毫秒):
@Test
public void whenValidateResponseTime_thenSuccess() {
when().get("/users/eugenp").then().time(lessThan(5000L));
}
如需按其他时间单位验证,使用带 TimeUnit
参数的 time()
方法:
@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
返回:
<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. XML 的 XPath 支持
使用 XPath 验证 XML 响应。示例:验证 first-name
包含特定字符串:
@Test
public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() {
post("/employees").then().assertThat().
body(hasXPath("/employees/employee/first-name", containsString("Ja")));
}
XPath 也支持 equalTo
匹配器的替代写法:
@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>
如需记录特定部分,替换 all()
为:
-
params()
:参数 -
body()
:请求体 -
headers()
:请求头 -
cookies()
:Cookie -
method()
:方法 -
path()
:路径
@Test
public void whenLogRequest_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);
}
此例中,仅当状态码非 200 时才会记录日志。
12. 总结
本文深入探讨了 REST-assured 框架的核心功能,展示了如何利用其强大特性测试 RESTful 服务并验证响应。通过这些技术,你可以:
- 简化 API 测试流程
- 灵活处理 JSON/XML 响应
- 精确控制请求与响应验证
- 高效调试问题(通过日志功能)
掌握这些技巧后,你的 API 测试效率将大幅提升,告别繁琐的手动验证。