接口測試 rest-assured 使用指南

轉載:https://testerhome.com/topics/7060

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

注意,如果您正在使用1.9.0或者更早的版本請參考舊文檔。

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

目錄

  1. 靜態導入方法
  2. 示例
    1. JSON 示例
    2. JSON Schema Validation
    3. XML 示例
    4. 高級用法
    5. XML
    6. JSON
    7. 其它示例
  3. 關于float和double
  4. 語法關注點?(語法糖)
  5. 獲得響應體信息
    1. 從已驗證的響應體中提取值
    2. JSON (使用 JsonPath)
    3. XML (使用XmlPath)
    4. 獲取某個路徑下的值
    5. Headers, cookies, status等
    6. 獲取全部header值
    7. 獲取全部cookie值
    8. 獲取詳細的cookie值
  6. 獲得響應信息
    1. 在驗證響應之后提取特定的值
    2. JSON (使用JsonPath)
    3. XML (使用XmlPath)
    4. 單獨使用路徑
    5. Headers, cookies, status等
    6. 獲取header
    7. 獲取cookie
    8. 獲取詳細的cookie值
  7. 指定請求數據
    1. 請求HTTP資源
    2. 參數化
    3. 多值參數
    4. 參數不賦值
    5. 路徑參數
    6. Cookie
    7. Header
    8. Content-Type
    9. 請求正文
  8. 驗證響應信息
    1. 響應體
    2. Cookie
    3. 狀態碼
    4. Header
    5. Content-Type
    6. 內容全匹配
    7. 關聯類型驗證
    8. 計算響應時間
  9. 認證
    1. 基本認證
    2. 搶占式的基本認證
    3. 受質詢的基本認證
    4. 摘要認證
    5. 表單認證
    6. CSRF
    7. OAuth
    8. OAuth1
    9. OAuth2
  10. Multi-part類型的表單數據
  11. 對象映射
    1. 序列化
    2. 基于Content-Type的序列化
    3. 由HashMap創建JSON
    4. 使用顯式序列化器
    5. 反序列化
    6. 基于Content-Type的反序列化
    7. 自定義content-type的反序列化
    8. 使用顯式反序列化器
    9. 配置
    10. 自定義
  12. 解析器
    1. 自定義解析器
    2. 默認解析器
  13. 默認值
  14. 模式復用
  15. 過濾器
    1. Response Builder
  16. 日志
    1. 請求日志
    2. 響應日志
    3. 認證失敗日志
  17. 根路徑
    1. 路徑參數
  18. Session支持
    1. Session過濾器
  19. SSL
    1. SSL無效的主機名
  20. URL編碼
  21. 代理(proxy)配置
    1. 靜態代理配置
    2. 請求規范代理配置
  22. 詳細配置
    1. 編碼配置
    2. 解碼配置
    3. Session配置
    4. 重定向(Redirect) DSL
    5. 網絡連接配置
    6. JSON配置
    7. HTTP客戶端配置
    8. SSL配置
    9. 參數配置
  23. Spring Mock Mvc模型
    1. Bootstrapping RestAssuredMockMvc
    2. 異步請求
    3. 添加Request Post Processors
    4. 添加Result Handlers
    5. 使用Result匹配器
    6. 攔截器
    7. Specifications
    8. 重置 RestAssuredMockMvc
    9. Spring MVC 身份認證
      1. 使用 Spring Security 測試
      2. 注入一個用戶
    10. 參數相關
  24. Scala支持
  25. Kotlin支持
  26. 更多

靜態導入方法

推薦大家從以下的類中靜態導入方法,以提高使用rest-assured的效率。

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

如果您想使用Json Schema?validation 還應該靜態導入這些方法:

io.restassured.module.jsv.JsonSchemaValidator.* 

更多使用方法參閱?Json Schema Validation?。

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

io.restassured.module.mockmvc.RestAssuredMockMvc.* 

示例

例一 - JSON

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

{
"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,你可以這樣做:

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

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

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

注意:?equalTo?和?hasItems?是 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 和 cookie 可以包含同名的多個值。

多個 header

要獲取header的所有值,您需要首先從Response對象中獲取Headers?對象。您需要首先從Response對象中獲取Headers對象。您可以使用Headers.getValues(
)方法返回一個具有所有header值的List列表。

多個 cookie

要獲取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,內容類型和正文。

響應體

請參閱使用示例,例如JSON?或?XML.

您還可以將響應正文映射到Java對象,單擊這里?了解詳細信息。

Cookie

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/hellohttp://code.google.com/p/rest-assured/wiki/Usage#Root_path)。其它的默認值也可以被指定:,附帶身份認證中的用戶名和密碼屬性。關于設置根路徑參考[這里](

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的參數以及重定向,日志,編碼器,解碼器,Session,ObjectMapper,Connection,SSL和ParamConfig?設置。 例子:

特定請求:

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.htmlencoding),可以指定默認的內容 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.server.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.server.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模塊定義在Response或MockMvcResponse中調用“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:

  • RestAssured
  • RestAssuredMockMvc Javadoc
  • Specification package

一些代碼示例:

  • REST Assured?tests
  • JsonPathTest
  • XmlPathTest

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/455338.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/455338.shtml
英文地址,請注明出處:http://en.pswp.cn/news/455338.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python中格式化字符串的作用_python中字符串格式化的意義(化妝)

格式 描述%%百分號標記 #就是輸出一個%%c字符及其ASCII碼%s字符串%d有符號整數(十進制)%u無符號整數(十進制)%o無符號整數(八進制)%x無符號整數(十六進制)%X無符號整數(十六進制大寫字符)%e浮點數字(科學計數法)%E浮點數字(科學計數法&#xff0c;用E代替e)%f浮點數字(用小數點…

MongoDB的快速手動安裝

就是關于MongoDB主從庫的安裝配置和啟動。網上關于MongoDB的安裝有大量的文章供大家學習。我這里提供一個Windows環境下MongoDB主從庫的快速手動安裝的方法&#xff0c;只需要三步即可。 先下載的安裝包&#xff0c;解壓縮后找到bin文件夾&#xff0c;將bin文件夾拷貝至你自己的…

MP4音頻解碼信息

文章轉載自&#xff1a;http://blog.csdn.net/flyingqr/archive/2010/02/02/5282600.aspx 版權歸原作者&#xff0c;編輯&#xff1a;小乙哥 MP4文件格式分為頭部和數據兩部分&#xff0c;頭部是由許多被稱作Atom的結構單元嵌套或排列而成&#xff0c;數據部分則完全為實際數據…

時序圖 分支_UML用例圖

UML用例圖用例圖有以下東東:用例參與者關聯系統邊界用例使用橢圓來表示&#xff0c;橢圓里邊寫上用例的名稱:這里的用例可以理解為一個動作或行為,或者一個對象。參與者用一個小人兒,在小人兒下面寫上參與者名稱,例如學生:關聯用一條線表示:把很多個用例放到一個大的矩形框里。…

Python腳本實現漢子轉拼音

起步 中華文化博大精深&#xff0c;是中華民族的財富&#xff0c;吸收和繼承發揚中 華文化&#xff0c;是現代每個炎黃子孫無可推卸的天職。 今天小編就交大家用python寫一個腳本,實現漢子和拼音之間的轉換 pinyin.py 漢字轉拼音,With Python Example: from pinyin impor…

MySQL innodb_table_stats表不存在的解決方法

在做實驗時&#xff0c;使用mysqldump命令報錯[rootlinux-mysql02 3306]# mysqldump -uroot -p123456 -S /u02/data/3306/mysql.sock -A -B --events | gzip > /opt/rep.sql.gzmysqldump: Got error: 1146: Table mysql.innodb_index_stats doesnt exist when using LOCK TA…

自定義封裝 banner 組件

1. 效果圖預覽 2.基本功能 一個簡單方便的輪播圖組件&#xff0c;基于viewpager 基礎上進行的封裝。可設置 項目中圖片&#xff0c;網絡圖片&#xff0c; View&#xff1b;支持循環自動播放&#xff0c;手勢滑動切換&#xff0c;item點擊事件,可設置 點點的樣式寬高、顏色、大小…

vb.net服務器啟動后cpu占用了70_記一次服務器被異常程序占用的解決過程(懷疑黑客攻擊)...

最近在跑實驗&#xff0c;但是突然發現程序運行變慢&#xff0c;然后top命令查看程序運行情況&#xff0c;發現有異常進程&#xff0c;名字叫 bash&#xff0c;占用 2400% CPU計算資源。剛開始懷疑是挖礦程序&#xff0c;因實驗室網絡IP為教育網公網&#xff0c;懷疑被攻擊&…

3gp文件格式研究 (轉windcao的專欄)

序言 06我開始做3gp文件的播放器,但是關于3gp的文檔太少了也很難找,在網友luxh的幫助下,我終于有了第一份關于3gp文件格式的文檔《ISO/IEC 14496-12&#xff0c;ISO媒體文件格式》.在此真心感謝luxh的貢獻.當然了是英文版的,有文檔就不錯了.為了便于查閱和理解,我把之后陸續找…

Android開發必用工具及其進階途徑

三百六十行&#xff0c;行行出狀元&#xff0c;怎么樣才能在Android行業中當個狀元了&#xff0c;開發過程中的高效、自我能力的提升顯得至關重要&#xff0c;步入IT行業更是要時時刻刻學習&#xff0c;新技術更新快&#xff0c;今天將介紹一下Android開發中必用工具及其進階途…

MySQL遇到check the manual that corresponds to your MySQL server version for the right syntax錯誤

原來是MySQL表中不能包含關鍵字 轉載于:https://www.cnblogs.com/flycoding/p/7088465.html

Python腳本實現圖片加水印

起步 圖片是指由圖形、圖像等構成的平面媒體,有形式的事物&#xff0c;我們看到的&#xff0c;是圖畫、照片、拓片等的統稱。 為了保護一些原創圖片的版權,某些時候我們需要在圖片上面,加上水印,當然你可以用Photoshop來做,只不過如果圖片數量過多,亦或者圖片的動態生成的時候…

yarn 怎么查看有多個job在跑_flink on yarn 模式下提示yarn資源不足問題分析

背景在實時計算平臺上通過YarnClient向yarn上提交flink任務時一直卡在那里&#xff0c;并在client端一直輸出如下日志&#xff1a;(YarnClusterDescriptor.java:1036)- Deployment took more than 60 seconds. Please check if the requested resources are available in the Y…

MPEG-2TS碼流編輯的原理及其應用(轉載

[作者&#xff1a;遼寧電視臺 趙季偉] 在當今數字媒體不斷發展、新媒體業務不斷涌現 的前提下&#xff0c;實踐證明襁褓中的新媒體只有兩種經營方略可供選擇&#xff1a;或是購買并集成整套節目&#xff0c;或是低成本深加工新節目&#xff0c;再不可能去按照傳統生產模式…

Python中的yield詳解

閱讀別人的python源碼時碰到了這個yield這個關鍵字&#xff0c;各種搜索終于搞懂了&#xff0c;在此做一下總結&#xff1a; 通常的for…in…循環中&#xff0c;in后面是一個數組&#xff0c;這個數組就是一個可迭代對象&#xff0c;類似的還有鏈表&#xff0c;字符串&#xf…

shell循環結構之while循環

while循環 1) while CONDITION; dostatementstatement<改變循環條件真假的語句>done 編寫腳本&#xff0c;計算1---100的和 #!/bin/bash#sum0i1while [ $i -le 100 ]; dolet sum$sum$ilet i$i1doneecho $sum2) while true; do statementstatementdone #!/bin/bash#while …

python 管道隊列_關于python:Multiprocessing-管道與隊列

Python的多處理程序包中的隊列和管道之間的根本區別是什么&#xff1f;在什么情況下應該選擇一種&#xff1f; 什么時候使用Pipe()有優勢&#xff1f; 什么時候使用Queue()有優勢&#xff1f;Pipe()只能有兩個端點。Queue()可以有多個生產者和消費者。何時使用它們如果需要兩個…

pip默認使用國內鏡像地址

很多小伙伴在ubuntu系統下,使用pip安裝會很慢 以為安裝源在國外服務器上面 今天小編就教大家配置成讓pip默認從國內源中尋找安裝包 首先CtrlAltT打開終端 進入家目錄 cd ~在家目錄中創建一個文件夾,命名為.pip mkdir .pip進入目錄,并創建一個名為pip.conf的文件 cd .pip…

“大型票務系統”和“實物電商系統”的數據庫選型

討論請移步至&#xff1a;http://www.zhiliaotech.com/ideajam/idea/detail/423 相關文章&#xff1a; 《今天你買到票了嗎&#xff1f;——從鐵道部12306.cn站點漫談電子商務站點的“海量事務快速處理”系統》 不能簡單套用“實物電商系統”對“大型票務系統”做需求分析 “大…

FLV文件格式(Z)(轉載)

剛才在看一些關于demux的東西&#xff0c;在處理flv格式的文件的時候&#xff0c;由于自己對flv文件的格式不了解&#xff0c;所以就比較云頭轉向&#xff0c;正好看到了一篇講述flv文件格式的文章&#xff0c;寫的比較明白&#xff0c;所以就轉過來了。O(∩_∩)O~flv頭文件比較…