rest-assured-doc接口自動化測試,數據驅動測試平臺

原文:https://github.com/rest-assured/rest-assured/wiki/Usage
本文github地址:https://github.com/RookieTester/rest-assured-docjavascript

注意,若是您正在使用1.9.0或者更早的版本請參考舊文檔php

REST Assured是一個能夠簡化HTTP Builder頂層 基於REST服務的測試過程的Java DSL(針對某一領域,具備受限表達性的一種計算機程序設計語言)。它支持發起POST,GET,PUT,DELETE,OPTIONS,PATCH和HEAD請求,而且能夠用來驗證和校對這些請求的響應信息。html

目錄

  1. 靜態導入方法
  2. 示例
  3. JSON 示例
  4. JSON Schema Validation
  5. XML 示例
  6. 高級用法
    1. XML
    2. JSON
  7. 其它示例
  8. 關於float和double
  9. 語法關注點 (語法糖)
  10. 得到響應體信息
  11. 從已驗證的響應體中提取值
  12. JSON (使用 JsonPath)
  13. XML (使用XmlPath)
  14. 獲取某個路徑下的值
  15. Headers, cookies, status等
    1. 獲取所有header值
    1. 獲取所有cookie值
    1. 獲取詳細的cookie值
  16. 得到響應信息
  17. 在驗證響應以後提取特定的值
  18. JSON (使用JsonPath)
  19. XML (使用XmlPath)
  20. 單獨使用路徑
  21. Headers, cookies, status等
    1. 獲取header
    1. 獲取cookie
    1. 獲取詳細的cookie值
  22. 指定請求數據
  23. 請求HTTP資源
  24. 參數化
  25. 多值參數
  26. 參數不賦值
  27. 路徑參數
  28. Cookie
  29. Header
  30. Content-Type
  31. 請求正文
  32. 驗證響應信息
  33. 響應體
  34. Cookie
  35. 狀態碼
  36. Header
  37. Content-Type
  38. 內容全匹配
  39. 關聯類型驗證
  40. 計算響應時間
  41. 認證
  42. 基本認證
    1. 搶佔式的基本認證
    1. 受質詢的基本認證
  43. 摘要認證
  44. 表單認證
    1. CSRF
  45. OAuth
    1. OAuth1
    1. OAuth2
  46. Multi-part類型的表單數據
  47. 對象映射
  48. 序列化
    1. 基於Content-Type的序列化
    1. 由HashMap建立JSON
    1. 使用顯式序列化器
  49. 反序列化
    1. 基於Content-Type的反序列化
    1. 自定義content-type的反序列化
    1. 使用顯式反序列化器
  50. 配置
  51. 自定義
  52. 解析器
  53. 自定義解析器
  54. 默認解析器
  55. 默認值
  56. 模式複用
  57. 過濾器
  58. Response Builder
  59. 日誌
  60. 請求日誌
  61. 響應日誌
  62. 認證失敗日誌
  63. 根路徑
  64. 路徑參數
  65. Session支持
  66. Session過濾器
  67. SSL
  68. SSL無效的主機名
  69. URL編碼
  70. 代理(proxy)配置
  71. 靜態代理配置
  72. 請求規範代理配置
  73. 詳細配置
  74. 編碼配置
  75. 解碼配置
  76. Session配置
  77. 重定向(Redirect) DSL
  78. 網絡鏈接配置
  79. JSON配置
  80. HTTP客戶端配置
  81. SSL配置
  82. 參數配置
  83. Spring Mock Mvc模型
  84. Bootstrapping RestAssuredMockMvc
  85. 異步請求
  86. 添加Request Post Processors
  87. 添加Result Handlers
  88. 使用Result匹配器
  89. 攔截器
  90. Specifications
  91. 重置 RestAssuredMockMvc
  92. Spring MVC 身份認證
    1. 使用 Spring Security 測試
    2. 注入一個用戶
  93. 參數相關
  94. Scala支持
  95. Kotlin支持
  96. 更多

靜態導入方法

推薦你們從如下的類中靜態導入方法,以提升使用rest-assured的效率。java

io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*

若是您想使用Json Schema validation 還應該靜態導入這些方法:git

io.restassured.module.jsv.JsonSchemaValidator.*

更多使用方法參閱 Json Schema Validationgithub

若是您正在使用SpringMVC,你可使用spring-mock-mvc 模型的Rest Assured DSL來對Spring的controller層進行單元測試。爲此須要從RestAssuredMockMvc靜態導入這些方法,而不是io.restassured.RestAssured:web

io.restassured.module.mockmvc.RestAssuredMockMvc.*

示例

例一 - JSON

假設某個get請求 (to http://localhost:8080/lotto) 返回JSON以下:spring

{
"lotto":{
 "lottoId":5,
 "winning-numbers":[2,45,34,23,7,5,3],
 "winners":[{
   "winnerId":23,
   "numbers":[2,45,34,23,3,5]
 },{
   "winnerId":54,
   "numbers":[52,3,12,11,18,22]
 }]
}
}

REST assured能夠幫您輕鬆地進行get請求並對響應信息進行處理。舉個例子,若是想要判斷lottoId的值是否等於5,你能夠這樣作:apache

get("/lotto").then().body("lotto.lottoId", equalTo(5));

又或許您想要檢查winnerId的取值包括23和54:編程

get("/lotto").then().body("lotto.winners.winnerId", hasItems(23, 54));

注意: equalTohasItems 是 Hamcrest matchers的方法,因此須要靜態導入 org.hamcrest.Matchers

注意這裏的"json path"語法使用的是Groovy的GPath標註法,不要和Jayway的JsonPath語法混淆。

以BigDecimal返回float和double類型

(譯者注:Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數進行精確的運算)

您能夠對rest-assured和JsonPath進行配置,使之以BigDecimal返回json裏的數值類型數據,而不是float或者double。能夠參考下面json文本:

{

    "price":12.12 

}

默認狀況下您驗證price字段是否等於float類型的12.12像這樣:

get("/price").then().body("price", is(12.12f));

可是若是想用rest-assured的JsonConfig來配置返回的全部的json數值都爲BigDecimal類型:

given().
        config(RestAssured.config().jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))).
when().
        get("/price").
then().
        body("price", is(new BigDecimal(12.12));

JSON Schema validation

自從 2.1.0 版本rest-assured開始支持Json Schema validation. 舉個例子,在classpath中放置如下的schema文件(譯者注:idea的話能夠放在resources目錄下),products-schema.json:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "title": "Product set",
    "type": "array",
    "items": {
        "title": "Product",
        "type": "object",
        "properties": {
            "id": {
                "description": "The unique identifier for a product",
                "type": "number"
            },
            "name": {
                "type": "string"
            },
            "price": {
                "type": "number",
                "minimum": 0,
                "exclusiveMinimum": true
            },
            "tags": {
                "type": "array",
                "items": {
                    "type": "string"
                },
                "minItems": 1,
                "uniqueItems": true
            },
            "dimensions": {
                "type": "object",
                "properties": {
                    "length": {"type": "number"},
                    "width": {"type": "number"},
                    "height": {"type": "number"}
                },
                "required": ["length", "width", "height"]
            },
            "warehouseLocation": {
                "description": "Coordinates of the warehouse with the product",
                "$ref": "http://json-schema.org/geo"
            }
        },
        "required": ["id", "name", "price"]
    }
}

您可使用這個schema驗證(/products)這個請求是否符合規範:

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));

matchesJsonSchemaInClasspath 靜態導入自 io.restassured.module.jsv.JsonSchemaValidator 而且咱們推薦從這個類中靜態導入全部的方法。然而爲了使用它須要依賴於json-schema-validator module 或者從這個網頁 下載 它, 又或者經過maven添加下面的依賴:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>json-schema-validator</artifactId>
    <version>3.0.1</version>
</dependency>

JSON Schema Validation 設置項

rest-assured的json-schema-validator module使用Francis Galiegue的json-schema-validator (fge) 庫來進行驗證。 若是您想配置使用基礎fge庫,你能夠像下面例子中:

// Given
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV4).freeze()).freeze();

// When
get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(jsonSchemaFactory));

using方法容許您進入jsonSchemaFactory的實例,rest-assured在驗證期間也會進行此操做。這種方式容許咱們對驗證進行細粒度的配置。

fge庫也容許驗證狀態是 checked或者unchecked(譯者注:表示不懂)。默認狀況,rest-assured使用checked驗證,可是若是你想要改變這種方式,您能夠提供一個matcher的JsonSchemaValidatorSettings實例。舉個例子:

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(settings().with().checkedValidation(false)));

這些settings方法靜態導入自 JsonSchemaValidatorSettings類。

Json Schema Validation的靜態配置

如今想象下您老是使用unchecked驗證,而且設置默認的json schema版本爲3。與其每次都在代碼裏進行設置,不如靜態地進行定義設置。舉個例子:

JsonSchemaValidator.settings = settings().with().jsonSchemaFactory(
        JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV3).freeze()).freeze()).
        and().with().checkedValidation(false);

get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));

如今任意一個由JsonSchemaValidator導入的matcher都會使用DRAFTV3做爲默認版本而且unchecked validation。

想要重置JsonSchemaValidator到默認設置僅僅須要調用reset方法:

JsonSchemaValidator.reset();

不使用rest-assured的Json Schema Validation

您也能夠在不依賴rest-assured的狀況下使用json-schema-validator module。如想要把json文本表示爲String類型的字符串,能夠這樣作:

import org.junit.Test;
import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import static org.hamcrest.MatcherAssert.assertThat;
 
public class JsonSchemaValidatorWithoutRestAssuredTest {
 
 
    @Test 
    public void validates_schema_in_classpath() {
        // Given
        String json = ... // Greeting response
 
        // Then
        assertThat(json, matchesJsonSchemaInClasspath("greeting-schema.json"));
    }
}

更多信息請參閱新手入門

匿名式的JSON根節點驗證

一個JSON文本並不老是有一個命名好的根屬性。這裏有個驗證這種JSON的例子:

[1, 2, 3]

一個匿名的JSON根屬性能夠經過使用$或者空字符串做爲路徑來識別。舉個例子,經過訪問http://localhost:8080/json這個地址能夠得到一個JSON文本,咱們可使用rest-assured驗證:

when().
     get("/json").
then().
     body("$", hasItems(1, 2, 3)); // An empty string "" would work as well

例2 - XML

XML能夠一種經過簡單的方式解析。假設一個POST請求http://localhost:8080/greetXML返回:

<greeting>
   <firstName>{params("firstName")}</firstName>
   <lastName>{params("lastName")}</lastName>
</greeting>

換言之,它在請求中返還了一個基於firstname和lastname請求參數的greeting節點。您能夠經過rest-assured輕易地展示和解析這個例子:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).

若是您想同時解析firstname和lastname能夠這樣作:

given().
         parameters("firstName", "John", "lastName", "Doe").
when().
         post("/greetXML").
then().
         body("greeting.firstName", equalTo("John")).
         body("greeting.lastName", equalTo("Doe"));

或者稍微簡短些:

with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body("greeting.firstName", equalTo("John"), "greeting.lastName", equalTo("Doe"));

這裏 的連接獲取有關語法的更多信息(它遵循 Groovy的 GPath 語法).

XML 命名空間

考慮到您須要使用io.restassured.config.XmlConfig聲明一個命名空間。舉個例子,有一個位於http://localhost:8080的資源namespace-example,返回以下的XML:

<foo xmlns:ns="http://localhost/">
  <bar>sudo </bar>
  <ns:bar>make me a sandwich!</ns:bar>
</foo>

能夠而後聲明http://localhost/這個URI而且驗證其響應:

given().
        config(RestAssured.config().xmlConfig(xmlConfig().declareNamespace("test", "http://localhost/"))).
when().
         get("/namespace-example").
then().
         body("foo.bar.text()", equalTo("sudo make me a sandwich!")).
         body(":foo.:bar.text()", equalTo("sudo ")).
         body("foo.test:bar.text()", equalTo("make me a sandwich!"));

這個路徑語法遵循Groovy的XmlSlurper語法。注意直到2.6.0的路徑語法都遵循Groovy的XmlSlurper語法。請看release notes能夠獲知2.6.0以前的版本語法是怎樣的。

XPath

您也可使用x-path來解析XML響應。舉個例子:

given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body(hasXPath("/greeting/firstName", containsString("Jo")));

或者

given().parameters("firstName", "John", "lastName", "Doe").post("/greetXML").then().body(hasXPath("/greeting/firstName[text()='John']"));

在XPath表達式中使用命名空間,你須要在配置中啓用這些選項:

given().
        config(RestAssured.config().xmlConfig(xmlConfig().with().namespaceAware(true))).
when().
         get("/package-db-xml").
then().
         body(hasXPath("/db:package-database", namespaceContext));

namespaceContext 是一個javax.xml.namespace.NamespaceContext的實例 。

Schema和DTD

XML響應體也能夠驗證爲一個XML Schema (XSD)或DTD.

XSD 例子

get("/carRecords").then().assertThat().body(matchesXsd(xsd));

DTD 例子

get("/videos").then().assertThat().body(matchesDtd(dtd));

matchesXsdmatchesDtd方法在Hamcrest matchers裏,你能夠從io.restassured.matcher.RestAssuredMatchers

導入。

例3 - 複雜的解析和驗證

這正是rest-assured閃光點所在!因爲rest-assured實現了Groovy,它能夠從Groovy集合的API的優勢中獲益。讓咱們從下面的Groovy例子中開始探索:

def words = ['ant', 'buffalo', 'cat', 'dinosaur']
def wordsWithSizeGreaterThanFour = words.findAll { it.length() > 4 }

在第一行,咱們簡單地定義了一個包含一些單詞的列表,不過第二行更加有趣。
這裏咱們檢索了列表裏的全部長度大於4的單詞,經過一個叫作findAll的Groovy閉包。
這個閉包有一個內部變量it,表明着列表中當前的元素。
結果是一個新的列表, wordsWithSizeGreaterThanFour,包含buffalo and dinosaur

這裏還有一些其它的有趣的方法,咱們也可使用在Groovy集合中:

  • find – 找到第一個匹配閉包謂詞(closure predicate)的元素
  • collect – 收集在集合裏的每一個元素都調用的閉包返回值(collect the return value of calling a closure on each item in a collection)
  • sum – 對集合裏的元素進行求和
  • max/min – 返回集合裏的最大值/最小值

因此咱們如何在使用rest-assured驗證XML和JSON響應時利用這些優勢?

XML示例

比方說咱們有個資源http://localhost:8080/shopping返回以下的XML:

<shopping>
      <category type="groceries">
        <item>Chocolate</item>
        <item>Coffee</item>
      </category>
      <category type="supplies">
        <item>Paper</item>
        <item quantity="4">Pens</item>
      </category>
      <category type="present">
        <item when="Aug 10">Kathryn's Birthday</item>
      </category>
</shopping>

又好比咱們想寫一個測試來檢驗類型爲groceries的category節點有Chocolate和Coffee這兩個項目。在rest-assured能夠這樣作:

when().
       get("/shopping").
then().
       body("shopping.category.find { it.@type == 'groceries' }.item", hasItems("Chocolate", "Coffee"));

這裏發生了什麼事?首先使用XML路徑shopping.category獲取了全部categoriy的一個列表。在這個列表中咱們又調用了一個方法,find,來返回有type這個屬性且該屬性值爲groceries的單個category節點。

在這個category上咱們接下來繼續收集全部相關聯的項目(item)。

因爲這裏與category相關聯的項目不止一個,因此會返回一個列表。接下來咱們經過Hamcrest matcher的hasItems方法來解析它。

可是若是咱們想取得一些項目(item)但又不想進行斷言驗證該怎麼辦?您能夠參考XmlPath:

// Get the response body as a String
String response = get("/shopping").asString();
// And get the groceries from the response. "from" is statically imported from the XmlPath class
List<String> groceries = from(response).getList("shopping.category.find { it.@type == 'groceries' }.item");

若是groceries是您對這個響應裏惟一的關注點,也可使用一個捷徑:

// Get the response body as a String
List<String> groceries = get("/shopping").path("shopping.category.find { it.@type == 'groceries' }.item");

深度優先搜索

實際上以前的例子咱們還能夠繼續簡化:

when().
       get("/shopping").
then().
       body("**.find { it.@type == 'groceries' }", hasItems("Chocolate", "Coffee"));

**是一種在XML文件中作深度優先搜索的捷徑。

咱們搜索第一個type屬性值等於"groceries"的節點。注意咱們沒有在"item"這個XML路徑結束。

緣由是在category節點返回一個列表的項目值時,自動調用了toString()這個方法(譯者注:這兩句有啥因果關係我沒搞懂)。

JSON示例

假設http://localhost:8080/store返回以下的JSON:

{  
   "store":{  
      "book":[  
         {  
            "author":"Nigel Rees",
            "category":"reference",
            "price":8.95,
            "title":"Sayings of the Century"
         },
         {  
            "author":"Evelyn Waugh",
            "category":"fiction",
            "price":12.99,
            "title":"Sword of Honour"
         },
         {  
            "author":"Herman Melville",
            "category":"fiction",
            "isbn":"0-553-21311-3",
            "price":8.99,
            "title":"Moby Dick"
         },
         {  
            "author":"J. R. R. Tolkien",
            "category":"fiction",
            "isbn":"0-395-19395-8",
            "price":22.99,
            "title":"The Lord of the Rings"
         }
      ]
   }
}

例1

在本例中咱們發起一個請求"/store",而且作了一個斷言:蒐集知足price字段值小於10的全部book數組裏的title字段,獲得了"Sayings of the Century"和"Moby Dick"這兩個結果:

when().
       get("/store").
then().
       body("store.book.findAll { it.price < 10 }.title", hasItems("Sayings of the Century", "Moby Dick"));

就像上面XML的例子,咱們使用閉包獲取全部price字段值低於10的book數組,而且返回相應的title字段值集合。

而後使用hasItems這個匹配器來斷言獲得咱們預期的結果。使用JsonPath 咱們能夠用下面的方法替代:

// Get the response body as a String
String response = get("/store").asString();
// And get all books with price < 10 from the response. "from" is statically imported from the JsonPath class
List<String> bookTitles = from(response).getList("store.book.findAll { it.price < 10 }.title");

例2

考慮下該如何斷言全部author字段值長度總和是否大於50的結果。

這是個挺難以回答的問題,也正展現了閉包和Groovy集合的強大之處。在rest-assured裏能夠:

when().
       get("/store");
then().
       body("store.book.author.collect { it.length() }.sum()", greaterThan(50));

首先咱們經過(store.book.author)獲得了全部的author字段值,而後使用閉包裏的方法{ it.length() }解析這個集合。

它所作的是對列表裏的每個author字段執行一次length()方法,而後返回一個新的列表。在這個列表中,咱們再調用sum()方法來求得字符長度的總和。

最終的結果是53,而且咱們使用greaterThan匹配器的斷言結果是大於50 。
可是實際上能夠繼續簡化這種寫法。能夠再次參考"words"這個例子

def words = ['ant', 'buffalo', 'cat', 'dinosaur']

Groovy有一個便利的方法能夠遍歷列表中的全部元素,使用*來調用。舉個例子:

def words = ['ant', 'buffalo', 'cat', 'dinosaur']
assert [3, 6, 3, 8] == words*.length()

Groovy返回了一個新的包含words中每一個字段字符長度的列表。咱們也能夠把rest-assured中的這個語法用在author列表中:

when().
       get("/store");
then().
       body("store.book.author*.length().sum()", greaterThan(50)).

固然咱們可使用JsonPath來獲取這個結果:

// Get the response body as a string
String response = get("/store").asString();
// Get the sum of all author length's as an int. "from" is again statically imported from the JsonPath class
int sumOfAllAuthorLengths = from(response).getInt("store.book.author*.length().sum()");
// We can also assert that the sum is equal to 53 as expected.
assertThat(sumOfAllAuthorLengths, is(53));

其它例子

Micha Kops曾寫過一篇很優秀的博客,裏面包含大量示例(包括可檢出的代碼)。您能夠由此進入試讀

Bas Dijkstra也開展過很多關於rest-assured的開源研究和資源。你能夠由此進入試讀,若是您想試用或者做出貢獻,他的github倉庫裏有些能夠嘗試的練習題。

關於float和double

浮點型數字必須和Java的基本類型"float"區分開。舉個例子,若是咱們看下面的JSON對象:

{

    "price":12.12 

}

以下的測試將會失敗,由於咱們在拿一個"double"在比較,而不是"float":

get("/price").then().assertThat().body("price", equalTo(12.12));

想用"float"比較的話寫法應該是:

get("/price").then().assertThat().body("price", equalTo(12.12f));

語法關注點

當閱讀rest-assured的博客時,你也許會看到許多使用"given / expect / when"語法的例子,舉個例子:

given().
        param("x", "y").
expect().
        body("lotto.lottoId", equalTo(5)).
when().
        get("/lotto");

這是一種「遺留語法」,這其實是rest-assured 1.x.版本用來寫測試用例的方式。然而這種運做方式令許多用戶迷惑甚至惱怒。這是由於一開始沒有把"given / when / then"做爲主要的技術來使用。因此rest-assured得2.0版本以前差很少不支持這種相似BDD-like測試的標準用法。"given / expect / when"在2.0仍然可用可是"given / when / then"可讀性更強因此在測試用例中更爲推薦。然而使用"given / expect / when"還有一個好處,就是全部的指望中的錯誤能夠在同時展現出來,這是新語法作不到的(自從預期結果放在了最後面)。這意味着若是你有多個預期結果想要檢驗你能夠:

given().
        param("x", "y").
expect().
        statusCode(400).
        body("lotto.lottoId", equalTo(6)).
when().
        get("/lotto");

rest-assured將同時報告狀態碼預期和響應體預期結果都是錯的。將這些用新語法重寫:

given().
        param("x", "y").
when().
        get("/lotto").
then().
        statusCode(400).
        body("lotto.lottoId", equalTo(6));

將會僅僅報告首個預期/斷言失敗的內容(好比預期狀態碼是400實際是200),第二個斷言將不執行。您將不得不從新運行這個用例以期獲取到第二個斷言的結果。

語法糖

rest-assured中另外一件值得注意的是,有些語法僅僅存在於語法糖中,舉個例子,"and"在一行代碼中使用能夠加強可讀性。

given().param("x", "y").and().header("z", "w").when().get("/something").then().assertThat().statusCode(200).and().body("x.y", equalTo("z"));

這等價於:

given().
        param("x", "y").
        header("z", "w").
when().
        get("/something").
then().
        statusCode(200).
        body("x.y", equalTo("z"));

得到響應體信息

你也能夠得到響應的內容。比方說你想經過發起一個get請求"/lotto"並獲取其響應內容。你能夠以多種方式:

InputStream stream = get("/lotto").asInputStream(); // Don't forget to close this one when you're done
byte[] byteArray = get("/lotto").asByteArray();
String json = get("/lotto").asString();

從已驗證的響應體中提取值

您能夠從響應信息中提取值,或者使用extract方法僅僅返回response自己的一個實例。如何你想獲取響應裏的值,並將其做爲接下來的請求內容,這會頗有用。下面是一個叫作title的資源返回的JSON數據:

{
     "title" : "My Title",
      "_links": {
              "self": { "href": "/title" },
              "next": { "href": "/title?page=2" }
           }
 }

想驗證內容類型是JSON格式且標題是My Title,可是還想要從中提取next的值並用來發起請求,下面是使用方法:

String nextTitleLink =
given().
        param("param_name", "param_value").
when().
        get("/title").
then().
        contentType(JSON).
        body("title", equalTo("My Title")).
extract().
        path("_links.next.href");

get(nextTitleLink). ..

若是您想提取多個值,也能夠考慮返回整個響應體:

Response response = 
given().
        param("param_name", "param_value").
when().
        get("/title").
then().
        contentType(JSON).
        body("title", equalTo("My Title")).
extract().
        response(); 

String nextTitleLink = response.path("_links.next.href");
String headerValue = response.header("headerName");

JSON (使用 JsonPath)

一旦咱們取得了響應體,可使用JsonPath來提取相應的數據:

int lottoId = from(json).getInt("lotto.lottoId");
List<Integer> winnerIds = from(json).get("lotto.winners.winnerId");

或者更高效一些:

JsonPath jsonPath = new JsonPath(json).setRoot("lotto");
int lottoId = jsonPath.getInt("lottoId");
List<Integer> winnerIds = jsonPath.get("winners.winnderId");

注意這裏咱們獨立地使用了JsonPath,而沒有依賴rest-assured自己的功能,看getting started guide 獲取更多信息。

JsonPath 配置

您能夠爲JsonPath配置反序列化對象(object de-serializers),舉個例子:

JsonPath jsonPath = new JsonPath(SOME_JSON).using(new JsonPathConfig("UTF-8"));

也能夠靜態配置好JsonPath,這樣全部的JsonPath實例都會共享這個配置:

JsonPath.config = new JsonPathConfig("UTF-8");

更多JsonPath的內容參照這篇博客

注意這裏的JsonPath基於Groovy的GPath,不要和Jayway的搞混了。

XML (使用XmlPath)

您也可使用XmlPath相應的功能:

String xml = post("/greetXML?firstName=John&lastName=Doe").andReturn().asString();
// Now use XmlPath to get the first and last name
String firstName = from(xml).get("greeting.firstName");
String lastName = from(xml).get("greeting.firstName");

// or a bit more efficiently:
XmlPath xmlPath = new XmlPath(xml).setRoot("greeting");
String firstName = xmlPath.get("firstName");
String lastName = xmlPath.get("lastName");

注意,您能夠獨立於rest-assured,單獨使用XmlPath的功能,更多信息參見getting started guide

XmlPath配置

你能夠配置XmlPath的對象反序列化器和字符編碼,舉個例子:

XmlPath xmlPath = new XmlPath(SOME_XML).using(new XmlPathConfig("UTF-8"));

也能夠靜態地配置XmlPath,使得全部的實例都能共享這套配置:

XmlPath.config = new XmlPathConfig("UTF-8");

更多關於XmlPath的信息參閱這篇博客

獲取某個路徑下的值

如您你只是想發起一個請求並返回一個路徑下的值,你可使用一個捷徑:

int lottoId = get("/lotto").path("lotto.lottoid");

rest-assured會基於響應體的content-type自動決定是使用JsonPath仍是XmlPath。若是這個類型在rest-assured沒有被定義,它將會自動到default parser中查找。你能夠自行(代碼指定)決定使用哪一種,好比:

String firstName = post("/greetXML?firstName=John&lastName=Doe").andReturn().xmlPath().getString("firstName");

xmlPath, jsonPathhtmlPath都是可選項。

Headers, cookies, status等

您也能夠獲取 header, cookie, 狀態行,狀態碼:

Response response = get("/lotto");

// 獲取全部 headers 信息
Headers allHeaders = response.getHeaders();

// 獲取單個 header 信息
String headerName = response.getHeader("headerName");

// 獲取全部 cookie 鍵值對
Map<String, String> allCookies = response.getCookies();

// 獲取單個 cookie 信息
String cookieValue = response.getCookie("cookieName");

// 獲取狀態行信息
String statusLine = response.getStatusLine();

// 獲取狀態碼信息
int statusCode = response.getStatusCode();

header 和 cookie 能夠包含同名的多個值。

多個 header

要獲取header的全部值,您須要首先從Response對象中獲取Headers 對象。您須要首先從Response對象中獲取Headers對象。您可使用Headers.getValues(
)方法返回一個具備全部header值的List列表。

要獲取cookie的全部值,您須要首先從Response對象中獲取Cookie對象。您可使用Cookie.getValues()方法獲取全部值,該方法返回包含全部Cookie值的List列表。

詳細的 Cookies 信息

若是您須要獲取Cookie的路徑或過時日期等詳細信息,您須要從REST Assured獲取一個detailed cookie。您可使用Response.getDetailedCookie(java.lang.String) 方法獲取單個Cookie,包括與給定名稱相關聯的全部屬性。

您還可使用Response.getDetailedCookies()方法獲取全部詳細的響應cookies

指定請求數據

除了指定請求參數,您還能夠指定header,Cookie,正文和Content Type。

請求HTTP資源

您一般經過調用request specification中的「HTTP方法」執行請求。例如:

when().get("/x"). ..;

其中get是HTTP請求方法。

從REST Assured 3.0.0開始,您能夠經過使用該方法爲請求使用任何HTTP動詞。

when().
       request("CONNECT", "/somewhere").
then().
       statusCode(200);

這將向服務器發送「鏈接」請求。

參數化

一般您能夠這樣指定參數:

given().
       param("param1", "value1").
       param("param2", "value2").
when().
       get("/something");

REST Assured將自動嘗試基於HTTP方法肯定哪一個參數類型(即查詢或表單參數)。在GET的狀況下,查詢參數將被自動使用,在POST的狀況下將使用表單參數。在某些狀況下,重要的是在PUT或POST中分離表單和查詢參數。你能夠這樣使用:

given().
       formParam("formParamName", "value1").
       queryParam("queryParamName", "value2").
when().
       post("/something");

參數也能夠url上進行設置:

..when().get("/name?firstName=John&lastName=Doe");

參數若是上傳的是文件,字節數組,輸入流或文本的能夠參照Multi-part類型的表單數據部分

多值參數

多值參數是每一個參數名稱具備多於一個值的參數(即,每一個名稱的值的列表)。您可使用var-args指定這些值:

given().param("myList", "value1", "value2"). ..

或者使用 list 列表:

List<String> values = new ArrayList<String>();
values.add("value1");
values.add("value2");

given().param("myList", values). ..

無值參數

您還能夠指定一個沒有值的請求或表單參數:

given().param("paramName"). ..

路徑參數

您還能夠在請求中指定所謂的路徑參數,例如

post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);

這些種類的路徑參數在REST Assured中稱爲「未命名路徑參數」,由於它們是基於索引的(hotelId將等於「My Hotel」,由於它是第一個佔位符)。

您還可使用命名路徑參數:

given().
        pathParam("hotelId", "My Hotel").
        pathParam("roomNumber", 23).
when(). 
        post("/reserve/{hotelId}/{roomNumber}").
then().
         ..

路徑參數使得更容易讀取請求路徑,且使請求路徑可以在具備不一樣參數值的許多測試中容易地重複使用。

從版本2.8.0開始,您能夠混合未賦值和賦值好的路徑參數:

given().
        pathParam("hotelId", "My Hotel").        
when(). 
        post("/reserve/{hotelId}/{roomNumber}", 23).
then().
         ..

這裏 roomNumber 的值My Hotel將被替換爲 23.

注意,指定太少或太多的參數將致使錯誤消息。對於高級用例,您能夠從[過濾器](#過濾器)添加,更改,刪除(甚至冗餘的路徑參數)。

一般模式下,您能夠經過如下方法指定Cookie:

given().cookie("username", "John").when().get("/cookie").then().body(equalTo("username"));

也能夠像這樣給cookie指定多個值:

given().cookie("cookieName", "value1", "value2"). ..

這將建立兩個cookie:cookieName = value1和cookieName = value2。

您還可使用如下方式指定詳細的Cookie:

Cookie someCookie = new Cookie.Builder("some_cookie", "some_value").setSecured(true).setComment("some comment").build();
given().cookie(someCookie).when().get("/cookie").then().assertThat().body(equalTo("x"));

或同時指定cookies:

Cookie cookie1 = Cookie.Builder("username", "John").setComment("comment 1").build();
Cookie cookie2 = Cookie.Builder("token", 1234).setComment("comment 2").build();
Cookies cookies = new Cookies(cookie1, cookie2);
given().cookies(cookies).when().get("/cookie").then().body(equalTo("username, token"));
given().header("MyHeader", "Something").and(). ..
given().headers("MyHeader", "Something", "MyOtherHeader", "SomethingElse").and(). ..

也能夠給一個headers指定多個值:

given().header("headerName", "value1", "value2"). ..

這將建立兩個header,headerName = value1和headerName = value2

Header 合併/覆蓋

默認狀況下,header合併能夠這樣:

given().header("x", "1").header("x", "2"). ..

請求將包含兩個標頭,「x:1」和「x:2」。您能夠在[HeaderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/HeaderConfig.html)的基礎上進行更改。例如:

given().
        config(RestAssuredConfig.config().headerConfig(headerConfig().overwriteHeadersWithName("x"))).
        header("x", "1").
        header("x", "2").
when().
        get("/something").
...

這意味着只有header 「x = 2」被髮送到服務器

Content Type

given().contentType(ContentType.TEXT). ..
given().contentType("application/json"). ..

請求正文

given().body("some body"). .. // Works for POST, PUT and DELETE requests
given().request().body("some body"). .. // More explicit (optional)
given().body(new byte[]{42}). .. // Works for POST, PUT and DELETE
given().request().body(new byte[]{42}). .. // More explicit (optional)

您還能夠將Java對象序列化爲JSON或XML。點擊這裏瞭解詳情。

驗證響應數據

您還能夠驗證狀態碼,狀態行,Cookie,headers,內容類型和正文。

響應體

請參閱使用示例,例如JSONXML.

您還能夠將響應正文映射到Java對象,單擊這裏 瞭解詳細信息。

get("/x").then().assertThat().cookie("cookieName", "cookieValue"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", "cookieValue2"). ..
get("/x").then().assertThat().cookies("cookieName1", "cookieValue1", "cookieName2", containsString("Value2")). ..

狀態碼

get("/x").then().assertThat().statusCode(200). ..
get("/x").then().assertThat().statusLine("something"). ..
get("/x").then().assertThat().statusLine(containsString("some")). ..

Header

get("/x").then().assertThat().header("headerName", "headerValue"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", "headerValue2"). ..
get("/x").then().assertThat().headers("headerName1", "headerValue1", "headerName2", containsString("Value2")). ..

還能夠在驗證頭時使用映射函數。 例如,假設您要驗證「Content-Length」頭部小於1000.而後,您可使用映射函數首先將頭值轉換爲int,而後在使用Hamcrest驗證前使用「整數」 匹配器:

get("/something").then().assertThat().header("Content-Length", Integer::parseInt, lessThan(1000));

Content-Type

get("/x").then().assertThat().contentType(ContentType.JSON). ..

內容全匹配

get("/x").then().assertThat().body(equalTo("something")). ..

關聯類型驗證

您可使用響應中的數據來驗證響應的另外一部分。 例如,從服務端返回的如下JSON:

{ "userId" : "some-id", "href" : "http://localhost:8080/some-id" }

您可能會注意到,「href」屬性以「userId」屬性的值結尾。 若是咱們想驗證這個,咱們能夠實現一個io.restassured.matcher.ResponseAwareMatcher,能夠:

get("/x").then().body("href", new ResponseAwareMatcher<Response>() {
                                  public Matcher<?> matcher(Response response) {
                                          return equalTo("http://localhost:8080/" + response.path("userId"));
                                  }
                       });

若是您使用Java 8,你可使用lambda表達式:

get("/x").then().body("href", response -> equalTo("http://localhost:8080/" + response.path("userId"));

有一些預約義的匹配器,您可使用在io.restassured.matcher.RestAssuredMatchers(或io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers若是使用spring-mock-mvc模塊)中定義。 例如:

get("/x").then().body("href", endsWithPath("userId"));

ResponseAwareMatchers也能夠與另外一個ResponseAwareMatcher或與Hamcrest Matcher組成。 例如:

get("/x").then().body("href", and(startsWith("http:/localhost:8080/"), endsWithPath("userId")));

and 方法是由io.restassured.matcher.ResponseAwareMatcherComposer靜態導入的。

計算響應時間

從 REST Assured 2.8.0開始支持測量響應時間,例如:

long timeInMs = get("/lotto").time()

或使用特定時間單位:

long timeInSeconds = get("/lotto").timeIn(SECONDS);

其中SECONDS只是一個標準的TimeUnit。 您還可使用DSL驗證:

when().
      get("/lotto").
then().
      time(lessThan(2000L)); // Milliseconds

when().
      get("/lotto").
then().
      time(lessThan(2L), SECONDS);

須要注意的是,您只能參考性地將這些測量數據與服務器請求處理時間相關聯(由於響應時間將包括HTTP往返和REST Assured處理時間等,不能作到十分準確)。

認證

REST assured還支持多種認證方案,例如OAuth,摘要,證書,表單和搶佔式基本認證。 您能夠爲每一個請求設置身份驗證:

given().auth().basic("username", "password"). ..

也能夠爲全部請求定義身份驗證:

RestAssured.authentication = basic("username", "password");

或者您也可使用 specification.

基本認證

有兩種類型的基本認證,搶佔和「受質詢的基本認證」。

搶佔式

服務器在某些狀況下給出未受權響應以前發送基本認證憑證,從而減小進行附加鏈接的開銷。 大多數狀況下能夠這麼使用:

given().auth().preemptive().basic("username", "password").when().get("/secured/hello").then().statusCode(200);

受質詢的基本認證

使用「受質詢的基本認證」時,REST Assured將不提供憑據,除非服務器已明確要求。 這意味着REST Assured將向服務器發出一個附加請求,以便進行質詢,而後再次處理相同的請求,但此時會在header中設置基本憑據。

given().auth().basic("username", "password").when().get("/secured/hello").then().statusCode(200);

摘要認證

目前只支持受質詢的摘要認證:

given().auth().digest("username", "password").when().get("/secured"). ..

表單認證

表單認證在互聯網上很是流行。 它一般與用戶在網頁上填寫其憑據(用戶名和密碼),而後在按某種類型的登陸按鈕時發起請求。 提供表單身份驗證基礎的一個很是簡單的HTML頁面可能以下所示

<html>
  <head>
    <title>Login</title>
  </head>

  <body>
    <form action="j_spring_security_check" method="POST">
      <table>
        <tr><td>User:&nbsp;</td><td><input type='text' name='j_username'></td></tr>
        <tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
          <tr><td colspan='2'><input name="submit" type="submit"/></td></tr>
       </table>
        </form>
      </body>
 </html>

也就是說 服務器指望用戶填寫「j_username」和「j_password」輸入字段,而後按「提交」登陸。 使用REST Assured,您能夠測試受表單身份驗證保護的服務,以下所示:

given().
        auth().form("John", "Doe").
when().
        get("/formAuth");
then().
        statusCode(200);

在REST中使用此類表單身份驗證時,會致使爲檢索包含登陸詳細信息的網頁而向服務器發出附加請求。 REST Assured將嘗試解析此頁面並查找兩個輸入字段(用戶名和密碼)以及表單操做的URI。 這可能失敗,取決於網頁的複雜性。 更好的選擇是在設置表單身份驗證時提供這些詳細信息。 在這種狀況下,能夠:

given().
        auth().form("John", "Doe", new FormAuthConfig("/j_spring_security_check", "j_username", "j_password")).
when().
        get("/formAuth");
then().
        statusCode(200);

這樣REST Assured不須要提出額外的請求並解析網頁。 還有一個預約義的FormAuthConfig稱爲springSecurity,若是你使用默認的Spring Security屬性,可使用它:

given().
        auth().form("John", "Doe", FormAuthConfig.springSecurity()).
when().
        get("/formAuth");
then().
        statusCode(200);

CSRF

現在,服務器要求請求中提供一個CSRF token是常有的事了,這能夠抵禦多種類型的攻擊。rest-assured支持解析並自動給服務器供應一個CSRF token。爲此,rest-assured必須先發起一個追加請求來解析該網站(的部份內容)。

你能夠經過下面的代碼啓用對CSRF的支持:

given().
        auth().form("John", "Doe", formAuthConfig().withAutoDetectionOfCsrf()).
when().
        get("/formAuth");
then().
        statusCode(200);

如今rest-assured將會自動嘗試偵測這個網站是否包含CSRF token機制。爲了使rest-assured的暴力破解更加順利,可能會提供一個CSRF域的名稱(這裏咱們假設咱們正在使用Spring的安全默認值,所以咱們可使用預約義的springSecurity表單認證配置):

given().
        auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf")).
when().
        get("/formAuth");
then().
        statusCode(200);

咱們至此已經告訴rest-assured去查找名爲"_csrf"的CSRF域了(然而這雖然會比自動偵測更快,也更容易出錯)。

默認狀況下CSRF值將會做爲一個請求參數,可是若是必要你也能夠配置其放在請求的header中:

given().
        auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf").sendCsrfTokenAsHeader()).
when().
        get("/formAuth");
then().
        statusCode(200);

OAuth

爲了使用OAuth1和OAuth2(關於查詢/請求參數簽名方面的機制),您須要添加Scribe到classpath中(若是你正在使用2.1.0或者更早以前版本的rest-assured,請參考舊版指南)。若是是maven請添加如下的依賴:

<dependency>
            <groupId>org.scribe</groupId>
            <artifactId>scribe</artifactId>
            <version>1.3.7</version>
            <scope>test</scope>
</dependency>

若是您沒有使用maven,能夠下載一個Scribe發行包並把它發在classpath下。

OAuth 1

OAuth1要求Scribe在classpath中。爲使用auth1的認證您能夠:

given().auth().oauth(..). ..

OAuth 2

自從2.5.0版本您能夠依賴於Scribe使用OAuth2的認證:

given().auth().oauth2(accessToken). ..

這將會把OAuth2的accessToken放入header中。想要更加顯式的操做能夠:

given().auth().preemptive().oauth2(accessToken). ..

這裏之因此存在given().auth().oauth2(..)這種語法是爲了向後兼容(作的是相同的事情)。若是你須要在請求參數中提供一個OAuth2 token,您須要把Scribe放在classpath下,接下來:

given().auth().oauth2(accessToken, OAuthSignature.QUERY_STRING). ..

自定義身份驗證

rest-assured容許您建立一個自定義的身份驗證。你能夠經過實現io.restassured.spi.AuthFilter接口,並做爲一個過濾器。假設您的安全機制,是由兩個header值相加而後組成一個新的叫作"AUTH"的header(固然這並不安全)。而後您能夠這樣作(Java 8的語法):

given().
        filter((requestSpec, responseSpec, ctx) -> {
            String header1 = requestSpec.getHeaders().getValue("header1");
            String header2 = requestSpec.getHeaders().getValue("header2");
            requestSpec.header("AUTH", header1 + header2);
            return ctx.next(requestSpec, responseSpec);
        }).
when().
        get("/customAuth").
then().
  statusCode(200);

使用AuthFilter而不是Filter的緣由是,當咱們執行given().auth().none(). ..相似這樣的操做時AuthFilters會被自動移除。

Multi-part 表單數據

一般咱們在向服務器傳輸大容量的數據時(譯者注:好比文件)會使用multipart表單數據技術。rest-assured提供了一種multiPart方法來辨別這到底是文件、二進制序列、輸入流仍是上傳的文本。表單中上傳一個文件能夠這樣:

given().
        multiPart(new File("/path/to/file")).
when().
        post("/upload");

它將會假設有一個control叫作"file"。在HTML中這意味着input標籤的屬性值爲file。爲了解釋得更清楚請看下面的HTML表單:

<form id="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file" size="40">
        <input type=submit value="Upload!">
</form>

在這個例子中control的名字就是一個屬性名爲file的input標籤。若是您使用的control名不是這個,須要指定:

given().
        multiPart("controlName", new File("/path/to/file")).
when().
        post("/upload");

在同一個請求中提供多個"multi-parts"事務也是可能的:

byte[] someData = ..
given().
        multiPart("controlName1", new File("/path/to/file")).
        multiPart("controlName2", "my_file_name.txt", someData).
        multiPart("controlName3", someJavaObject, "application/json").
when().
        post("/upload");

更多高級使用方法可使用MultiPartSpecBuilder。舉個例子:

Greeting greeting = new Greeting();
greeting.setFirstName("John");
greeting.setLastName("Doe");

given().
        multiPart(new MultiPartSpecBuilder(greeting, ObjectMapperType.JACKSON_2)
                .fileName("greeting.json")
                .controlName("text")
                .mimeType("application/vnd.custom+json").build()).
when().
        post("/multipart/json").
then().
        statusCode(200);

你能夠經過使用MultiPartConfig指定默認的control名和文件名。舉個例子:

given().config(config().multiPartConfig(multiPartConfig().defaultControlName("something-else"))). ..

這就會默認把control名配置爲"something-else"而不是"file"。

其它用法請查閱 這篇博客

對象映射

rest-assured支持從JSON和XML中映射Java對象。映射JSON須要classpath中有Jackson或者Gson才能使用,XML則須要JAXB。

序列化

假設咱們有下面的Java對象:

public class Message {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

您須要將這個對象序列化爲JSON併發送到請求中。能夠有多種方式:

基於Content-Type的序列化

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json").
       body(message).
when().
      post("/message");

在這個例子裏,因爲請求中的content-type被設置爲"application/json",rest-assured也就會把對象序列化爲JSON。rest-assured首先會在您的classpath中尋找Jackson,若是沒有則使用Gson。若是您把請求中的content-type修改成"application/xml",rest-assured將會使用JAXB把對象序列化爲XML。若是沒有指定content-type,rest-assured會按照如下的優先級進行序列化:

  1. 使用Jackson 2將對象序列化爲JSON(Faster Jackson (databind))
  2. 使用Jackson將對象序列化爲JSON(databind)
  3. 使用Gson將對象序列化爲JSON
  4. 使用JAXB將對象序列化爲XML

rest-assured也關心content-type的字符集(charset)等等。

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json; charset=UTF-16").
       body(message).
when().
      post("/message");

您也能夠把Message這個實例序列化爲一個表單參數:

Message message = new Message();
message.setMessage("My messagee");
given().
       contentType("application/json; charset=UTF-16").
       formParam("param1", message).
when().
      post("/message");

這個message對象將會被實例化爲utf-16編碼的JSON(若是有Jackson或者Gson)。

由HashMap建立JSON

您也能夠提供一個Map,由此rest-assured能夠建立一個JSON。

Map<String, Object>  jsonAsMap = new HashMap<>();
jsonAsMap.put("firstName", "John");
jsonAsMap.put("lastName", "Doe");

given().
        contentType(JSON).
        body(jsonAsMap).
when().
        post("/somewhere").
then().
        statusCode(200);

這將會產生一個JSON數據(JSON payload):

{ "firstName" : "John", "lastName" : "Doe" }

使用顯式序列化器

若是您的classpath中同時有多個對象、或者不考慮content-type的設置,能夠顯示地指定一個序列化器。

Message message = new Message();
message.setMessage("My messagee");
given().
       body(message, ObjectMapperType.JAXB).
when().
      post("/message");

在這個例子中message對象將會被JAXB序列化爲一個XML。

反序列化

讓咱們再次假設咱們有如下的Java對象:

public class Message {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

咱們須要把響應體反序列化爲一個Message對象。

基於Content-Type的反序列化

假設服務端返回一個這樣的JSON:

{"message":"My message"}

將它反序列化爲一個Message對象:

Message message = get("/message").as(Message.class);

爲此響應體的content-type必須是"application/json"(或者其它包含「json」的類型)。若是服務端返回:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<message>
      <message>My message</message>
</message>

且content-type是"application/xml",代碼能夠徹底不用修改:

Message message = get("/message").as(Message.class);

自定義類型content-type反序列化

若是服務端返回一個自定義的content-type,假設是"application/something",你仍然想使用rest-assured的對象映射的話,這有兩種方法。你可使用顯式指定的方法或者爲自定義的content-type註冊一個解析器:

Message message = expect().parser("application/something", Parser.XML).when().get("/message").as(Message.class);

Message message = expect().defaultParser(Parser.XML).when().get("/message").as(Message.class);

你也能夠註冊一個默認解析器,或者靜態式註冊一個自定義的解析器,也可使用模式(specifications)

使用顯式反序列化器

若是您的classpath下同時有多個對象或者不在乎響應體的content-type,你可使用顯示的反序列化器。

Message message = get("/message").as(Message.class, ObjectMapperType.GSON);

配置

您可使用ObjectMapperConfig配置預約義的對象映射,並傳遞給細節配置。舉個例子,你能夠將GSON的命名策略改成LowerCaseWithUnderscores(譯者注:一種策略,將大寫字母改成小寫字母並添加下劃線):

RestAssured.config = RestAssuredConfig.config().objectMapperConfig(objectMapperConfig().gsonObjectMapperFactory(
                new GsonObjectMapperFactory() {
                    public Gson create(Class cls, String charset) {
                        return new GsonBuilder().setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
                    }
                }
        ));

這裏爲GSON、JAXB、Jackson和Faster Jackson都預約義了用來映射實例的工廠。

自定義

默認狀況下rest-assured將會掃描classpath中各類各樣的對象映射。若是您想要集成一種對象映射,默認是不支持的,若是您已經作好了封裝,能夠實現io.restassured.mapper.ObjectMapper 接口。告訴rest-assured使用您的對象映射或者將其做爲body方法裏的第二個參數:

given().body(myJavaObject, myObjectMapper).when().post("..")

或者您能夠靜態式定義:

RestAssured.config = RestAssuredConfig.config().objectMapperConfig(new ObjectMapperConfig(myObjectMapper));

更多例子參閱這裏

自定義解析器

rest-assured提供了預約義的解析器,例如HTML、XML和JSON的。可是您能夠經過註冊預置的解析器來解析如今不支持的內容類型:

RestAssured.registerParser(<content-type>, <parser>);

例如註冊一個能夠解析'application/vnd.uoml+xml'類型的XML解析器:

RestAssured.registerParser("application/vnd.uoml+xml", Parser.XML);

您也能夠註銷一個解析器:

RestAssured.unregisterParser("application/vnd.uoml+xml");

解析器能夠指定於每一個請求中:

get(..).then().using().parser("application/vnd.uoml+xml", Parser.XML). ..;

而後使用模式

默認解析器

有時若是響應中不包含任何content-type,指定一個默認的解析器會頗有用。

RestAssured.defaultParser = Parser.JSON;

你也能夠爲一次請求指定默認的解析器:

get("/x").then().using().defaultParser(Parser.JSON). ..

或者使用響應體模式

默認值

rest-assured發起請求時默認使用localhost的8080端口.若是你須要換個端口:

given().port(80). ..

或者簡單些:

..when().get("http://myhost.org:80/doSomething");

您也能夠改變默認的基本URI、基本路徑、端口和認證scheme:

RestAssured.baseURI = "http://myhost.org";
RestAssured.port = 80;
RestAssured.basePath = "/resource";
RestAssured.authentication = basic("username", "password");
RestAssured.rootPath = "x.y.z";

這意味着相似get("/hello")這樣的請求實際上會被解析爲http://myhost.org:80/resource/hello,附帶身份認證中的用戶名和密碼屬性。關於設置根路徑參考這裏。其它的默認值也能夠被指定:

RestAssured.filters(..); // List of default filters
RestAssured.requestSpecification = .. // Default request specification
RestAssured.responseSpecification = .. // Default response specification
RestAssured.urlEncodingEnabled = .. // Specify if Rest Assured should URL encoding the parameters
RestAssured.defaultParser = .. // Specify a default parser for response bodies if no registered parser can handle data of the response content-type
RestAssured.registerParser(..) // Specify a parser for the given content-type
RestAssured.unregisterParser(..) // Unregister a parser for the given content-type

您能夠設置重置爲標準的baseURI (localhost),basePath(空),標準端口(8080),標準根路徑(「」),默認身份認證scheme(none)和URL編碼啓用(true):

RestAssured.reset();

模式複用

與其複製一份響應的斷言或者請求參數(的代碼)到另外一個測試用例中,咱們可使用 RequestSpecBuilder或者ResponseSpecBuilder定義一個規範提案。

舉個例子,在多個測試用例中,咱們都涉及到這樣的內容:判斷響應碼是否爲200,JSON數組x.y的長度是不是2,您能夠定義一個ResponseSpecBuilder:

ResponseSpecBuilder builder = new ResponseSpecBuilder();
builder.expectStatusCode(200);
builder.expectBody("x.y.size()", is(2));
ResponseSpecification responseSpec = builder.build();

// Now you can re-use the "responseSpec" in many different tests:
when().
       get("/something").
then().
       spec(responseSpec).
       body("x.y.z", equalTo("something"));

在這個例子中須要重用的數據定義併合並在"responseSpec",而且僅當全部預期都經過時用例才能經過。

您也能夠將相同的請求參數重用:

RequestSpecBuilder builder = new RequestSpecBuilder();
builder.addParam("parameter1", "parameterValue");
builder.addHeader("header1", "headerValue");
RequestSpecification requestSpec = builder.build();
  
given().
        spec(requestSpec).
        param("parameter2", "paramValue").
when().
        get("/something").
then().
        body("x.y.z", equalTo("something"));

這裏請求數據合併在"requestSpec"中,由此上面例子中實際請求參數包括兩個("parameter1" 和 "parameter2")和一個header("header1")。

過濾器

過濾器會在請求實際發起以前偵測和改變該請求的內容,也能夠在響應體實際返回以前攔截並改變。您能夠將其理解爲AOP中的around advice(譯者注:能夠自行搜索切片編程)。過濾器也能夠用在認證scheme、session管理、日誌中。建立一個過濾器須要實現io.restassured.filter.Filter接口。使用過濾器:

given().filter(new MyFilter()). ..

rest-assured提供了幾個過濾器:

  1. io.restassured.filter.log.RequestLoggingFilter: 能夠打印出請求模式的細節。
  2. io.restassured.filter.log.ResponseLoggingFilter: 能夠打印響應信息的細節若是響應體的狀態碼匹配given方法的參數。
  3. io.restassured.filter.log.ErrorLoggingFilter: 若是發生了異常(狀態碼在400和500之間),過濾器將會打印響應的內容。

Response Builder

若是您想要經過一個過濾器改變Response ,可使用ResponseBuilder建立一個基於原始response的新實例。好比把原始響應體改成something:

Response newResponse = new ResponseBuilder().clone(originalResponse).setBody("Something").build();

日誌

在大量的用例中,打印出響應或者請求的細節將有助於建立正確的預期、發送準確的請求。爲此您可使用rest-assured預約義的過濾器,或者使用其中的快捷方法。

請求日誌

自1.5版本起rest-assured支持對特定的請求打日誌,以前的作法是使用RequestLoggingFilter(譯者注:應該是經過過濾器進行控制)。注意打印日誌後HTTP Builder和HTTP Client會添加額外的header內容。這個過濾器能夠只記錄特定請求的特定細節。換句話說,你能夠不關注RequestLoggingFilter記錄的有關實際往服務端發送的內容。由於隨後的其它過濾器會在日誌記錄後改變這個請求。若是你想要記錄實際發送的內容,參閱HTTP Client logging docs or use an external tool such Wireshark,或者使用第三方工具例如Wireshark。示例以下:

given().log().all(). .. // Log all request specification details including parameters, headers and body
given().log().params(). .. // Log only the parameters of the request
given().log().body(). .. // Log only the request body
given().log().headers(). .. // Log only the request headers
given().log().cookies(). .. // Log only the request cookies
given().log().method(). .. // Log only the request method
given().log().path(). .. // Log only the request path

響應日誌

若是您想打印除了狀態碼之外的響應信息,能夠:

get("/x").then().log().body() ..

這樣作,不管是否有異常錯誤發生,都會打印出響應信息。若是您但願只有當錯誤發生時纔打印響應信息,能夠:

get("/x").then().log().ifError(). ..

您也能夠記錄響應裏包括狀態碼、header、cookie的全部細節:

get("/x").then().log().all(). ..

也能夠只記錄狀態碼、header或者cookie:

get("/x").then().log().statusLine(). .. // Only log the status line
get("/x").then().log().headers(). .. // Only log the response headers
get("/x").then().log().cookies(). .. // Only log the response cookies

您也能夠配置爲僅當狀態碼匹配某個值時纔打印響應體:

get("/x").then().log().ifStatusCodeIsEqualTo(302). .. // Only log if the status code is equal to 302
get("/x").then().log().ifStatusCodeMatches(matcher). .. // Only log if the status code matches the supplied Hamcrest matcher

認證失敗日誌

自rest-assured2.3.1版本起,您能夠僅當認證失敗時記錄請求或者響應的日誌。爲請求打日誌:

given().log().ifValidationFails(). ..

爲響應打日誌:

.. .then().log().ifValidationFails(). ..

同時啓用對請求和響應的認證失敗記錄,可使用LogConfig:

given().config(RestAssured.config().logConfig(logConfig().enableLoggingOfRequestAndResponseIfValidationFails(HEADERS))). ..

認證失敗,僅記錄header。

還有種針對全部請求的簡單寫法:

RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();

根路徑

爲避免在body方法裏使用重複的路徑,您能夠指定一個根路徑:

when().
         get("/something").
then().
         body("x.y.firstName", is(..)).
         body("x.y.lastName", is(..)).
         body("x.y.age", is(..)).
         body("x.y.gender", is(..));

使用根路徑:

when().
        get("/something").
then().
         root("x.y"). // You can also use the "root" method
         body("firstName", is(..)).
         body("lastName", is(..)).
         body("age", is(..)).
         body("gender", is(..));

也能夠設置一個默認的根路徑:

RestAssured.rootPath = "x.y";

在許多高級用例中,在根路徑上附加一些參數也頗有用。您可使用appendRoot方法:

when().
         get("/jsonStore").
then().
         root("store.%s", withArgs("book")).
         body("category.size()", equalTo(4)).
         appendRoot("%s.%s", withArgs("author", "size()")).
         body(withNoArgs(), equalTo(4));

也能夠對根路徑進行拆分:

when().
         get("/jsonStore").
then().
         root("store.category").
         body("size()", equalTo(4)).
         detachRoot("category").
         body("size()", equalTo(1));

路徑參數

在預約義的路徑包含變量時,路徑參數會頗有用。舉個例子:

String someSubPath = "else";
int index = 1;
get("/x").then().body("something.%s[%d]", withArgs(someSubPath, index), equalTo("some value")). ..

將會對"something.else[0](譯者注:這裏只是舉個例子)"是否等於"some value"進行判斷。

另外一種用法是,若是您有複雜的根路徑

when().
       get("/x").
then().
       root("filters.filterConfig[%d].filterConfigGroups.find { it.name == 'GroupName' }.includes").
       body(withArgs(0), hasItem("first")).
       body(withArgs(1), hasItem("second")).
       ..

路徑參數遵循Java的標準格式語法

注意withArgs方法能夠從io.restassured.RestAssured類中靜態導入。

有時當全部在根路徑中指定的參數都已經驗證過了,只想要驗證一個不含多餘參數的body時,可使用withNoArgs

when().
         get("/jsonStore").
then().
         root("store.%s", withArgs("book")).
         body("category.size()", equalTo(4)).
         appendRoot("%s.%s", withArgs("author", "size()")).
         body(withNoArgs(), equalTo(4));

Session支持

rest-assured提供了一套簡單的管理session的方式。您能夠在DSL(譯者注:領域特定語言)中預約義一個session的id值:

given().sessionId("1234"). ..

上面的代碼其實是這個的簡寫:

given().cookie("JSESSIONID", "1234"). ..

您也能夠爲全部的請求指定一個默認的sessionId

RestAssured.sessionId = "1234";

默認狀況下session id的名字是JSESSIONID,可是您能夠經過使用SessionConfig來修改:

RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));

您也能夠指定一個sessionid並在其它用例中複用,使用RequestSpecBuilder

RequestSpecBuilder spec = new RequestSpecBuilder().setSessionId("value1").build();
   
// Make the first request with session id equal to value1
given().spec(spec). .. 
// Make the second request with session id equal to value1
given().spec(spec). ..

從響應對象中獲取一個session id:

String sessionId = get("/something").sessionId();

Session過濾器

2.0.0版本起您可使用session filter截獲並提供一個session,舉個例子:

SessionFilter sessionFilter = new SessionFilter();

given().
          auth().form("John", "Doe").
          filter(sessionFilter).
when().
          get("/formAuth").
then().
          statusCode(200);


given().
          filter(sessionFilter). // Reuse the same session filter instance to automatically apply the session id from the previous response
when().
          get("/x").
then().
          statusCode(200);

要想獲取由SessionFilter截獲的session id:

String sessionId = sessionFilter.getSessionId();

SSL

在多數場景下,SSL能順利運轉,這多虧於HTTP Builder和HTTP Client。若是服務端使用了無效的證書,然而有些例子下仍是會出錯。最簡單的方法您可使用"relaxed HTTPs validation":

given().relaxedHTTPSValidation().when().get("https://some_server.com"). ..

也能夠爲全部的請求靜態定義這個配置:

RestAssured.useRelaxedHTTPSValidation();

或者放在請求指定中進行復用。

這將會假定咱們使用SSL協議。想要改變須要覆蓋relaxedHTTPSValidation方法:

given().relaxedHTTPSValidation("TLS").when().get("https://some_server.com"). ..

您也能夠建立一個Java keystore文件並執行一些細粒度的操做。這並不難,首先閱讀指南,而後在rest-assured中使用keystore:

given().keystore("/pathToJksInClassPath", <password>). ..

或者爲每個請求指定:

RestAssured.keystore("/pathToJksInClassPath", <password>);

您也能夠定義一個可複用的keystore。

若是您已經使用密碼載入了一個keystore,能夠將其做爲可信的keystore:

RestAssured.trustStore(keystore);

也能夠找到一些相關的示例。

有關更多SSL的高級配置參閱SSL Configuration

SSL 無效主機名

若是證書指定了一個無效的主機名,而且您無需建立和導入一個keystore。自2.2.0起您能夠:

RestAssured.config = RestAssured.config().sslConfig(sslConfig().allowAllHostnames());

讓全部請求支持全部的主機名。

given().config(RestAssured.config().sslConfig(sslConfig().allowAllHostnames()). .. ;

對於單個請求。

注意若是您使用了"relaxed HTTPs validation",那麼allowAllHostnames將會默認啓用。

URL 編碼

因爲rest-assured提供了優秀的自動編碼,一般您無需考慮URL編碼的事情。在有些用例中仍然須要關閉URL編碼的功能。其中一個緣由是在使用rest-assured以前可能你已經作過一次編碼。爲防止兩次URL編碼您須要告訴rest-assured禁用這個功能。

String response = given().urlEncodingEnabled(false).get("https://jira.atlassian.com:443/rest/api/2.0.alpha1/search?jql=project%20=%20BAM%20AND%20issuetype%20=%20Bug").asString();
..

或者

RestAssured.baseURI = "https://jira.atlassian.com";
RestAssured.port = 443;
RestAssured.urlEncodingEnabled = false;
final String query = "project%20=%20BAM%20AND%20issuetype%20=%20Bug";
String response = get("/rest/api/2.0.alpha1/search?jql={q}", query);
..

代理(proxy)配置

從版本2.3.2開始REST Assured能夠更好地支持代理。 例如,若是你有一個代理在localhost端口8888你能夠作:

given().proxy("localhost", 8888). ..

若是服務器在本地環境中運行,能夠不指定主機名:

given().proxy(8888). .. // Will assume localhost

要使用HTTPS,須要提供第三個參數(scheme)或使用io.restassured.specification.ProxySpecification。 例如:

given().proxy(host("localhost").withScheme("https")). ..

其中host從io.restassured.specification.ProxySpecification靜態導入。

從版本2.7.0開始,您還能夠爲代理指定搶佔式基自己份驗證。 例如:

given().proxy(auth("username", "password")).when() ..

其中auth是從io.restassured.specification.ProxySpecification靜態導入的,也能夠將身份驗證與不一樣的host相結合:

given().proxy(host("http://myhost.org").withAuth("username", "password")). ..

靜態代理配置

能夠爲全部請求配置靜態代理,例如:

RestAssured.proxy("localhost", 8888);

或者:

RestAssured.proxy = host("localhost").withPort(8888);

請求規範代理配置

您還能夠建立請求規範並在其中指定代理:

RequestSpecification specification = new RequestSpecBuilder().setProxy("localhost").build();
given().spec(specification). ..

詳細配置

詳細配置由RestAssuredConfig實例提供,您可使用它配置HTTP Client的參數以及重定向日誌編碼器解碼器SessionObjectMapperConnectionSSLParamConfig 設置。 例子:

特定請求:

given().config(RestAssured.config().redirect(redirectConfig().followRedirects(false))). ..

或使用RequestSpecBuilder:

RequestSpecification spec = new RequestSpecBuilder().setConfig(RestAssured.config().redirect(redirectConfig().followRedirects(false))).build();

或全部請求:

RestAssured.config = config().redirect(redirectConfig().followRedirects(true).and().maxRedirects(0));

config() and newConfig() can be statically imported from io.restassured.config.RestAssuredConfig.

編碼配置

使用[EncoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/EncoderConfig.html),能夠指定默認的內容encoding charset(若是沒有在content-type頭中指定)和查詢參數charset爲全部請求。 若是未指定內容字符集,則使用ISO-8859-1,若是未指定查詢參數字符集,則使用UTF-8。 用法示例:

RestAssured.config = RestAssured.config().encoderConfig(encoderConfig().defaultContentCharset("US-ASCII"));

若是沒有經過使用EncoderConfig中的defaultCharsetForContentType方法爲此內容類型顯式定義字符集,那麼還能夠指定用於特定內容類型的編碼器字符集。 例如:

RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));

這將假設明確指定一個字符集的「application / xml」內容類型的UTF-16編碼。 默認狀況下,「application / json」被指定爲使用「UTF-8」做爲默認內容類型,這是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。

避免自動將字符集添加到content-type標頭

默認狀況下,REST Assured會自動添加字符集標頭。 要徹底禁用這個,你能夠這樣配置EncoderConfig

RestAssured.config = RestAssured.config(config().encoderConfig(encoderConfig().appendDefaultContentCharsetToContentTypeIfUndefined(false));

解碼配置

使用[DecoderConfig](http://static.javadoc.io/io.rest-assured/rest-assured/3.0.1/io/restassured/config/DecoderConfig.html),您能夠將默認響應內容解碼字符集設置爲 全部響應。 若是您指望使用不一樣於ISO-8859-1的內容字符集(這是默認字符集),而且響應未在內容類型標頭中定義字符集,那麼這將很是有用。 用法示例:

RestAssured.config = RestAssured.config().decoderConfig(decoderConfig().defaultContentCharset("UTF-8"));

您還可使用「DecoderConfig」來指定要應用哪些內容解碼器。 當你這樣作時,「Accept-Encoding」頭部將被自動添加到請求中,而且響應主體將被自動解碼。 默認狀況下,啓用GZIP和DEFLATE解碼器。 例如要刪除GZIP解碼但保留DEFLATE解碼,您能夠執行如下操做:
您還可使用「DecoderConfig」來指定要應用哪些內容解碼器。 當你這樣作時,「Accept-Encoding」頭部將被自動添加到請求中,而且響應主體將被自動解碼。 默認狀況下,啓用GZIP和DEFLATE解碼器。 例如要刪除GZIP解碼但保留DEFLATE解碼,您能夠執行如下操做:

given().config(RestAssured.config().decoderConfig(decoderConfig().contentDecoders(DEFLATE))). ..

若是沒有經過使用DecoderConfig中的「defaultCharsetForContentType」方法爲此內容類型明肯定義字符集,則還能夠指定要用於特定內容類型的解碼器字符集。 例如:

RestAssured.config = config(config().decoderConfig(decoderConfig().defaultCharsetForContentType("UTF-16", "application/xml")));

這將假設明確指定一個字符集的「application / xml」內容類型的UTF-16編碼。 默認狀況下,「application / json」使用「UTF-8」做爲默認字符集,這是由[RFC4627](https://www.ietf.org/rfc/rfc4627.txt)指定的。

Session配置

使用Session配置,您能夠配置REST Assured使用的默認session ID名稱。 默認session id名稱是JSESSIONID,若是應用程序中的名稱不一樣,而且您但願使用REST Assured的[會話支持](#Session_support),您只需更改它。 用法:

RestAssured.config = RestAssured.config().sessionConfig(new SessionConfig().sessionIdName("phpsessionid"));

重定向 DSL

重定向配置可使用DSL指定。 例如。

given().redirects().max(12).and().redirects().follow(true).when(). ..

網絡鏈接配置

讓您配置REST Assured的鏈接設置。 例如,若是要在每一個響應後強制關閉Apache HTTP Client鏈接。 若是您在響應中使用少許數據進行大量快速連續請求,則可能須要執行此操做。然而,若是你正在下載(尤爲是大量的)分塊,你毫不能每一個響應後關閉鏈接的數據。 默認狀況下,鏈接在每一個響應後不關閉。

RestAssured.config = RestAssured.config().connectionConfig(connectionConfig().closeIdleConnectionsAfterEachResponse());

Json Config

JsonPathConfig容許您在REST Assured或 JsonPath使用時配置Json設置。 它讓你配置如何處理JSON數字。

RestAssured.config = RestAssured.config().jsonConfig(jsonConfig().numberReturnType(NumberReturnType.BIG_DECIMAL))

HTTP Client 配置

爲REST Assured將在執行請求時使用的HTTP Client實例配置屬性。 默認狀況下,REST Assured會爲每一個「given」語句建立一個新的http Client實例。 要配置重用,請執行如下操做:

RestAssured.config = RestAssured.config().httpClient(httpClientConfig().reuseHttpClientInstance());

您還可使用httpClientFactory方法提供自定義HTTP Client實例,例如:

RestAssured.config = RestAssured.config().httpClient(httpClientConfig().httpClientFactory(
         new HttpClientConfig.HttpClientFactory() {

            @Override
            public HttpClient createHttpClient() {
                return new SystemDefaultHttpClient();
            }
        }));

注意,目前你須要提供一個AbstractHttpClient的實例.

也能夠配置默認參數等。

SSL 配置

SSLConfig 容許您指定更高級的SSL配置,如信任庫,密鑰庫類型和主機名驗證器。 例如:

RestAssured.config = RestAssured.config().sslConfig(sslConfig().with().keystoreType(<type>).and().strictHostnames());

參數配置

ParamConfig 容許您配置在「衝突」時,更新不一樣的參數。 默認狀況下,全部參數都將合併,所以若是您執行如下操做:

given().queryParam("param1", "value1").queryParam("param1", "value2").when().get("/x"). ...

REST Assured將發送一個查詢字符串param1 = value1&param1 = value2
若是這不是您想要的,你能夠配置REST Assured爲* replace *值代替:

given().
        config(config().paramConfig(paramConfig().queryParamsUpdateStrategy(REPLACE))).
        queryParam("param1", "value1").
        queryParam("param1", "value2").
when().
        get("/x"). ..

REST Assured如今將替換param1的值爲value2(由於它是最後寫的),而不是將它們合併在一塊兒。 您也能夠爲全部參數類型的每種類型配置統一的更新策略

given().config(config().paramConfig(paramConfig().replaceAllParameters())). ..

也支持在[Spring Mock Mvc模塊](# Spring Mock Mvc 模塊)(配置[MockMvcParamConfig](http://static.javadoc.io/io.restassured/spring-mock -mvc / 3.0.1 / io / restassured / module / mockmvc / config / MockMvcParamConfig.html)。

Spring Mock Mvc 模塊

REST Assured 2.2.0引入了對[Spring Mock Mvc](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/web/servlet/MockMvc.html)的支持,使用 spring-mock-mvc模塊。 這意味着您能夠單元測試Spring Mvc控制器。 例如給出如下Spring控制器:

@Controller
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping(value = "/greeting", method = GET)
    public @ResponseBody Greeting greeting(
            @RequestParam(value="name", required=false, defaultValue="World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}

你可使用[RestAssuredMockMvc](http://static.javadoc.io/io.restassured/spring-mock-mvc/3.0.1/io/restassured/module/mockmvc/RestAssuredMockMvc.html)來測試它,像這樣:

given().
        standaloneSetup(new GreetingController()).
        param("name", "Johan").
when().
        get("/greeting").
then().
        statusCode(200).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));

即它與標準的REST Assured語法很是類似。 這使得運行測試真的很快,而且比標準的REST Assured更容易引導環境和使用mock。 你經常使用的標準REST Assured中的大多數東西均可以使用RestAssured Mock Mvc。 例如(某些)配置,靜態規範,日誌等等。要使用它,你須要依賴於Spring Mock Mvc模塊:

<dependency>
      <groupId>io.rest-assured</groupId>
      <artifactId>spring-mock-mvc</artifactId>
      <version>3.0.1</version>
      <scope>test</scope>
</dependency>

或者[下載](http://dl.bintray.com/johanhaleby/generic/spring-mock-mvc-3.0.1-dist.zip)。

Bootstrapping RestAssuredMockMvc

靜態導入方法:

io.restassured.module.mockmvc.RestAssuredMockMvc.*
io.restassured.module.mockmvc.matcher.RestAssuredMockMvcMatchers.*

有關其餘靜態導入,請參閱文檔的[靜態導入](#static-imports)部分。

爲了使用RestAssuredMockMvc啓動測試,您須要使用一組控制器,MockMvc實例或Spring的WebApplicationContext來初始化它。您能夠對單個請求執行此操做,如上例所示:

given().standaloneSetup(new GreetingController()). ..

也可使用靜態方法:

RestAssuredMockMvc.standaloneSetup(new GreetingController());

若是靜態定義,則沒必要在DSL中指定任何控制器(或MockMvc或WebApplicationContext實例)。這意味着前面的例子能夠寫成:

given().
        param("name", "Johan").
when().
        get("/greeting").
then().
        statusCode(200).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));

異步請求

從版本2.5.0 RestAssuredMockMvc支持異步請求。例如,假設有如下控制器

@Controller
public class PostAsyncController {

    @RequestMapping(value = "/stringBody", method = POST)
    public @ResponseBody
    Callable<String> stringBody(final @RequestBody String body) {
        return new Callable<String>() {
            public String call() throws Exception {
                return body;
            }
        };
    }
}

你能夠這樣測試:

given().
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

默認超時爲1秒。也可使用DSL更改超時:

given().
        body("a string").
when().
        async().with().timeout(20, TimeUnit.SECONDS).post("/stringBody").
then().
        body(equalTo("a string"));

還可使用AsyncConfig),例如:

given().
        config(config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS))).
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

withTimeout是從io.restassured.module.mockmvc.config.AsyncConfig靜態導入的,只是建立一個具備給定超時的AsyncConfig的快捷方式。全局應用配置以應用於全部請求:

RestAssuredMockMvc.config = RestAssuredMockMvc.config().asyncConfig(withTimeout(100, TimeUnit.MILLISECONDS));

// Request 1
given().
        body("a string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

// Request 2
given().
        body("another string").
when().
        async().post("/stringBody").
then().
        body(equalTo("a string"));

請求1和2如今將使用默認超時100毫秒。

添加請求後處理器

Spring MockMvc已經對請求後處理器作了支持,而且您也能夠在RestAssuredMockMvc中使用。舉個例子:

given().postProcessors(myPostProcessor1, myPostProcessor2). ..

請注意,推薦從org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors(例如認證相關的 RequestPostProcessors)加入請求後處理器時,直接使用auth方法,這樣可讀性更強,固然結果是同樣的:

given().auth().with(httpBasic("username", "password")). ..

這裏的httpBasic靜態導入自SecurityMockMvcRequestPostProcessor

添加結果處理器

Spring MockMvc對結果處理器 作了支持,您也能夠在RestAssuredMockMvc中使用。比方說您想要使用原生的MockMvc日誌功能:

.. .then().apply(print()). ..

這裏的print靜態導入自org.springframework.test.web.servlet.result.MockMvcResultHandlers。請注意若是您正在使用2.6.0或更早版本的rest-assured,須要這樣使用結果處理器:

given().resultHandlers(print()). ..

可是rest-assured2.8.0起不推薦使用這種語法。

使用結果匹配器

Spring MockMvc提供了許多結果匹配器,您能夠從中獲益。RestAssuredMockMvc也對其中必要的功能進行支持。舉個例子,基於某種緣由您想要使用結果匹配器驗證狀態碼是否等於200:

given().
        param("name", "Johan").
when().
        get("/greeting").
then().
        assertThat(status().isOk()).
        body("id", equalTo(1)).
        body("content", equalTo("Hello, Johan!"));

這裏的status靜態導入自org.springframework.test.web.servlet.result.MockMvcResultMatchers。注意,您可使用expect方法,功能上和assertThat同樣,可是更接近原生的MockMvc的語法。

攔截器

您也能夠在請求(譯者注:這裏指mock請求)曝光以前截住並改變MockHttpServletRequestBuilder。您須要先定義一個MockHttpServletRequestBuilderInterceptor,並在RestAssuredMockMvc中使用:

given().interceptor(myInterceptor). ..

Specifications

正如標準的Res​​t Assured,你可使用specifications,以便更好地重用。請注意,RestAssuredMockMvc的請求規範構建器稱爲MockMvcRequestSpecBuilder。一樣的ResponseSpecBuilder 也能夠在RestAssuredMockMvc中使用。規格能夠靜態定義,就像標準的Res​​t Assured同樣。例如:

RestAssuredMockMvc.requestSpecification = new MockMvcRequestSpecBuilder().addQueryParam("name", "Johan").build();
RestAssuredMockMvc.responseSpecification = new ResponseSpecBuilder().expectStatusCode(200).expectBody("content", equalTo("Hello, Johan!")).build();

given().
        standaloneSetup(new GreetingController()).
when().
        get("/greeting").
then().
        body("id", equalTo(1));

重置 RestAssuredMockMvc

若是您使用了任何靜態配置,您能夠經過調用RestAssuredMockMvc.reset()方法輕鬆地將RestAssuredMockMvc重置爲其默認狀態。

Spring MVC認證

spring-mock-mvc的版本`2.3.0'支持身份驗證。例如:

given().auth().principal(..). ..

一些認證方法須要Spring安全在類路徑(可選)。也能夠靜態定義認證:

RestAssuredMockMvc.authentication = principal("username", "password");

其中principal方法是從RestAssuredMockMvc靜態導入的。還能夠在請求構建器中定義認證方案:

MockMvcRequestSpecification spec = new MockMvcRequestSpecBuilder.setAuth(principal("username", "password")).build();

使用 Spring Security 測試

從版本2.5.0也有更好的支持Spring Security。若是你在類路徑中有spring-security-test,你能夠這樣作:

given().auth().with(httpBasic("username", "password")). ..

其中httpBasic是從SecurityMockMvcRequestPostProcessor靜態導入的。這將對請求應用基本認證。爲了這個工做,你須要應用SecurityMockMvcConfigurer到MockMvc實例。您能夠手動執行此操做:

MockMvc mvc = MockMvcBuilders.webAppContextSetup(context).apply(SecurityMockMvcConfigurers.springSecurity()).build();

or RESTAssuredMockMvc will automatically try to apply the springSecurity configurer automatically if you initalize it with an instance of AbstractMockMvcBuilder, for example when configuring a "web app context":

given().webAppContextSetup(context).auth().with(httpBasic("username", "password")). ..

Here's a full example:

import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.context.WebApplicationContext;

import static io.restassured.module.mockmvc.RestAssuredMockMvc.given;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyConfiguration.class)
@WebAppConfiguration
public class BasicAuthExample {

    @Autowired
    private WebApplicationContext context;

    @Before public void
    rest_assured_is_initialized_with_the_web_application_context_before_each_test() {
        RestAssuredMockMvc.webAppContextSetup(context);
    }

    @After public void
    rest_assured_is_reset_after_each_test() {
        RestAssuredMockMvc.reset();
    }

    @Test public void
    basic_auth_example() {
        given().
                auth().with(httpBasic("username", "password")).
        when().
                get("/secured/x").
        then().
                statusCode(200).
                expect(authenticated().withUsername("username"));
    }
}

You can also define authentication for all request, for example:

RestAssuredMockMvc.authentication = with(httpBasic("username", "password"));

where with is statically imported from io.restassured.module.mockmvc.RestAssuredMockMvc. It's also possible to use a request specification.

注入一個用戶

也可使用Spring Security測試註釋,例如@WithMockUser@WithUserDetails。例如,假設您想測試此控制器:

@Controller
public class UserAwareController {

    @RequestMapping(value = "/user-aware", method = GET)
    public
    @ResponseBody
    String userAware(@AuthenticationPrincipal User user) {
        if (user == null || !user.getUsername().equals("authorized_user")) {
            throw new IllegalArgumentException("Not authorized");
        }

        return "Success");
    }
}

您能夠看到`userAware'方法須要一個 User 做爲參數,咱們讓Spring Security使用@AuthenticationPrincipal 注入它。要生成測試用戶,咱們能夠這樣作:

@WithMockUser(username = "authorized_user")
@Test public void
spring_security_mock_annotations_example() {
    given().
            webAppContextSetup(context).
     when().
            get("/user-aware").
     then().
            statusCode(200).
            body(equalTo("Success")).
            expect(authenticated().withUsername("authorized_user"));
}

注意,也能夠不使用註釋,而是使用RequestPostProcessor ,例如SecurityMockMvcRequestPostProcessors#user(java.lang.String).

參數相關

MockMvc沒有區分不一樣類型的參數,因此paramformParamqueryParam目前只是委託給MockMvc中的param。 formParam自動添加application / x-www-form-urlencoded內容類型的頭部,就像標準的Res​​t Assured同樣。

Scala支持

REST Assured 2.6.0引入了將「別名」添加到「then」方法的scala-support模塊定義在ResponseMockMvcResponse中調用「Then」。這樣作的緣由是then在未來多是Scala中的保留關鍵字,而且當使用具備此名稱的方法時,編譯器會發出警告。要啓用Then,只需從scala-support模塊導入io.restassured.module.scala.RestAssuredSupport.AddThenToResponse類。例如:

import io.restassured.RestAssured.when
import io.restassured.module.scala.RestAssuredSupport.AddThenToResponse
import org.hamcrest.Matchers.equalTo
import org.junit.Test

@Test
def `trying out rest assured in scala with implicit conversion`() {
  when().
          get("/greetJSON").
  Then().
          statusCode(200).
          body("key", equalTo("value"))
}

注意:一樣支持 Spring Mock Mvc Module.

能夠像這樣使用它:

SBT:

libraryDependencies += "io.rest-assured" % "scala-support" % "3.0.1"

Maven:

<dependency>
    <groupId>io.rest-assured</groupId>
    <artifactId>scala-support</artifactId>
    <version>3.0.1</version>
    <scope>test</scope>
</dependency>

Gradle:

testCompile 'io.rest-assured:scala-support:3.0.1'

No build manager:

手動下載 distribution file

Kotlin支持

Kotlin是由JetBrains開發的一種語言,它與Java和REST Assured很是好地集成。當使用它與REST Assured有一件事,必須逃避when,由於它是Kotlin中的保留關鍵字。例如:

Test fun kotlin_rest_assured_example() {
    given().
            param("firstName", "Johan").
            param("lastName", "Haleby").
    `when`().
            get("/greeting").
    then().
            statusCode(200).
            body("greeting.firstName", equalTo("Johan")).
            body("greeting.lastName", equalTo("Haleby"))
}

爲了解決這個問題,建立一個extension function,建立一個別名爲when時叫作When

fun RequestSpecification.When(): RequestSpecification {
    return this.`when`()
}

代碼如今能夠像這樣寫:

Test fun kotlin_rest_assured_example() {
    given().
            param("firstName", "Johan").
            param("lastName", "Haleby").
    When().
            get("/greeting").
    then().
            statusCode(200).
            body("greeting.firstName", equalTo("Johan")).
            body("greeting.lastName", equalTo("Haleby"))
}

注意,咱們不須要任何轉義。有關更多詳細信息,請參閱this博客文章。

更多信息

其餘相關信息,請參考 javadoc:

一些代碼示例:

若是你須要支持,能夠加入 mailing list.

若是須要專業支持,請聯繫 johanhaleby.

相關文章
相關標籤/搜索