REST-assured 使用指南

HTTP Client-Side,JSON,REST,Testing
Remote
0
03:16 PM · Dec 01 ,2025

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.305.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, CONNECTOPTIONS 請求:

@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 bodyString

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 服務和驗證其響應的最重要功能。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.