1. 簡介
REST-assured 的設計目標是簡化 REST API 的測試和驗證,並深受動態語言(如 Ruby 和 Groovy)中使用的測試技術的影響。
該庫對 HTTP 提供了強大的支持,當然包括 HTTP 動詞和標準 HTTP 操作,但同時也遠不止這些基礎內容。
在本指南中,我們將 探索 REST-assured,並使用 Hamcrest 進行斷言。如果您不熟悉 Hamcrest,請先閲讀:使用 Hamcrest 進行測試。
此外,要了解 REST-assured 的高級使用案例,請查看我們的其他文章:
- 使用 REST-assured 和 Groovy
- 使用 REST-assured 進行 JSON Schema 驗證
- 使用 REST-assured 中的參數、標頭和 Cookie
現在,讓我們從一個簡單的示例開始。
2. 簡單示例測試
在我們開始之前,請確保我們的測試具有以下靜態導入:
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*
我們需要這樣才能使測試簡單,並輕鬆訪問主要的 API。
現在,讓我們從一個簡單的示例開始——一個基本投注系統,它會公開一些遊戲的數據:
{
"id": "390",
"data": {
"leagueId": 35,
"homeTeam": "Norway",
"visitingTeam": "England",
},
"odds": [ {
"price": "1.30",
"name": "1"
},
{
"price": "5.25",
"name": "X"
}]
}
假設這個 JSON 響應來自對 http://localhost:8080/events?id=390 這一端點進行的調用。
現在,讓我們使用 REST-assured 來驗證響應 JSON 的一些有趣特性:
@Test
public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() {
get("/events?id=390").then().statusCode(200).assertThat()
.body("data.leagueId", equalTo(35));
所以,我們在這裏驗證的是,對端點 /events?id=390 的調用會返回一個包含 JSON String 的主體,該主體中的 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,我們在 rest-assured 依賴 在 pom.xml 文件中:
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
REST-assured 利用 Hamcrest 匹配器(Matchers)的強大功能來執行斷言,因此我們必須包含 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 暴露了上述服務,那麼我們可以使用 REST-assured 進行驗證:
when().get("/json").then().body("$", hasItems(1, 2, 3));
這種方法也有效:
when().get("/json").then().body("", hasItems(1, 2, 3));
5. 浮點數和雙精度數
當我們使用 REST-assured 測試我們的 REST 服務時,我們需要理解 JSON 響應中浮點數映射到原始類型 float
使用 float 類型的用法與 double 類型用法不能互換:
{
"odd": {
"price": "1.30",
"ck": 12.2,
"name": "1"
}
}
如果我們運行以下測試以獲取 ck 的值:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));
即使我們正在測試的值與響應中的值相等,此測試也可能失敗。這是因為我們正在將它與 double 進行比較,而不是與 float 進行比較。
要使它正常工作,我們必須明確地將操作數指定給 equalTo 匹配器方法,該操作數是一個 float:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));
6. 指定請求方法
通常,我們會通過調用如 get() 這樣的方法來執行請求,對應於我們想要使用的請求方法。
此外,我們可以使用 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() 方法指定 body 。
因此,為了創建一個新的 Odd ,通過發送 POST 請求,我們可以運行:
@Test
public void whenRequestedPost_thenCreated() {
with().body(new Odd(5.25f, 1, 13.1f, "X"))
.when()
.request("POST", "/odds/new")
.then()
.statusCode(201);
}
作為 body 發送的 Odd 對象將自動轉換為 JSON。 還可以傳遞任何我們想要作為我們的 POST body 的 String。
7. 默認值配置
我們可以配置大量的默認值用於測試:
@BeforeEach
public void setup() {
RestAssured.baseURI = "https://api.github.com";
RestAssured.port = 443;
}
在這裏,我們設置了 base URI 和端口用於我們的請求。除了這些,我們還可以配置 base path、root path 和認證。
請注意,我們還可以通過使用以下代碼重置為標準 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);
}
注意,在上面的示例中:
- time() 用於獲取響應時間(毫秒)
- timeIn() 用於獲取響應時間(指定的時間單位)
8.1. 驗證響應時間
我們還可以使用簡單的 long Matcher 來驗證響應時間(毫秒):
@Test
public void whenValidateResponseTime_thenSuccess() {
when().get("/users/eugenp").then().time(lessThan(5000L));
}
如果我們想以不同的時間單位驗證響應時間,則將使用 time() 匹配器,並使用第二個 TimeUnit 參數:
@Test
public void whenValidateResponseTimeInSeconds_thenSuccess(){
when().get("/users/eugenp").then().time(lessThan(5L),TimeUnit.SECONDS);
}
9. XML 響應驗證
不僅可以驗證 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"));
}
我們還可以通過 鏈接使用 body 匹配器 來驗證所有值是否與預期值匹配:
@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 for XML
我們可以使用 XPath 驗證我們的響應。 考慮一個執行對 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. Logging Test Details
11.1. Log Request Details
首先,讓我們看看如何使用 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()、method()、path():
@Test
public void whenLogRequest_thenOK() {
given().log().params()
.when().get("/users/eugenp")
.then().statusCode(200);
}
請注意,其他可能使用的庫或過濾器可能會更改實際發送到服務器的內容,因此僅應將其用於記錄初始請求規範。
11.2. Log Response Details
同樣,我們可以記錄響應詳情。
在下面的示例中,我們僅記錄響應正文:
@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. Log Response if Condition Occurs
我們還具有在發生錯誤或狀態碼與給定值匹配時僅記錄響應的選項:
@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. Log if Validation Fails
我們還可以僅在驗證失敗時記錄請求和響應:
@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 服務和驗證其響應的最重要功能。