轉載: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請求,并且可以用來驗證和校對這些請求的響應信息。
目錄
- 靜態導入方法
- 示例
- JSON 示例
- JSON Schema Validation
- XML 示例
- 高級用法
- XML
- JSON
- 其它示例
- 關于float和double
- 語法關注點?(語法糖)
- 獲得響應體信息
- 從已驗證的響應體中提取值
- JSON (使用 JsonPath)
- XML (使用XmlPath)
- 獲取某個路徑下的值
- Headers, cookies, status等
- 獲取全部header值
- 獲取全部cookie值
- 獲取詳細的cookie值
- 獲得響應信息
- 在驗證響應之后提取特定的值
- JSON (使用JsonPath)
- XML (使用XmlPath)
- 單獨使用路徑
- Headers, cookies, status等
- 獲取header
- 獲取cookie
- 獲取詳細的cookie值
- 指定請求數據
- 請求HTTP資源
- 參數化
- 多值參數
- 參數不賦值
- 路徑參數
- Cookie
- Header
- Content-Type
- 請求正文
- 驗證響應信息
- 響應體
- Cookie
- 狀態碼
- Header
- Content-Type
- 內容全匹配
- 關聯類型驗證
- 計算響應時間
- 認證
- 基本認證
- 搶占式的基本認證
- 受質詢的基本認證
- 摘要認證
- 表單認證
- CSRF
- OAuth
- OAuth1
- OAuth2
- Multi-part類型的表單數據
- 對象映射
- 序列化
- 基于Content-Type的序列化
- 由HashMap創建JSON
- 使用顯式序列化器
- 反序列化
- 基于Content-Type的反序列化
- 自定義content-type的反序列化
- 使用顯式反序列化器
- 配置
- 自定義
- 解析器
- 自定義解析器
- 默認解析器
- 默認值
- 模式復用
- 過濾器
- Response Builder
- 日志
- 請求日志
- 響應日志
- 認證失敗日志
- 根路徑
- 路徑參數
- Session支持
- Session過濾器
- SSL
- SSL無效的主機名
- URL編碼
- 代理(proxy)配置
- 靜態代理配置
- 請求規范代理配置
- 詳細配置
- 編碼配置
- 解碼配置
- Session配置
- 重定向(Redirect) DSL
- 網絡連接配置
- JSON配置
- HTTP客戶端配置
- SSL配置
- 參數配置
- Spring Mock Mvc模型
- Bootstrapping RestAssuredMockMvc
- 異步請求
- 添加Request Post Processors
- 添加Result Handlers
- 使用Result匹配器
- 攔截器
- Specifications
- 重置 RestAssuredMockMvc
- Spring MVC 身份認證
- 使用 Spring Security 測試
- 注入一個用戶
- 參數相關
- Scala支持
- Kotlin支持
- 更多
靜態導入方法
推薦大家從以下的類中靜態導入方法,以提高使用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));
?
matchesXsd
和matchesDtd
方法在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
,?jsonPath
和htmlPath
都是可選項。
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
通常模式下,您可以通過以下方法指定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"));
Header
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: </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會按照以下的優先級進行序列化:
- 使用Jackson 2將對象序列化為JSON(Faster Jackson (databind))
- 使用Jackson將對象序列化為JSON(databind)
- 使用Gson將對象序列化為JSON
- 使用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提供了幾個過濾器:
io.restassured.filter.log.RequestLoggingFilter
: 可以打印出請求模式的細節。io.restassured.filter.log.ResponseLoggingFilter
: 可以打印響應信息的細節如果響應體的狀態碼匹配given方法的參數。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沒有區分不同類型的參數,所以param
,formParam
和queryParam
目前只是委托給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