一、静态导入
为了有效的使用rest-assured,官网推荐从下列class中静态导入方法:
1 io.restassured.RestAssured.* 2 io.restassured.matcher.RestAssuredMatchers.* 3 org.hamcrest.Matchers.*
如果想使用 Json Schema validation(验证),还需要静态导入下面的方法:
1 io.restassured.module.jsv.JsonSchemaValidator.*
如果我们正在使用的是 Spring MVC ,我们可以使用 spring--mock-mvc模型的rest-assured DSL来对Spring 的controllers层进行单元测试。为此我们需要从 RestAssuredMockMvc 中导入下面的方法,还不是从 io.restassured.RestAssured 导入:
1 io.restassured.module.mockmvc.RestAssuredMockMvc.*
二、使用实例
1.Json实例
假设 GET请求(to http://localhost:8080/lotto)返回下面的Json:
1 { 2 "lotto":{ 3 "lottoId":5, 4 "winning-numbers":[2,45,34,23,7,5,3], 5 "winners":[{ 6 "winnerId":23, 7 "numbers":[2,45,34,23,3,5] 8 },{ 9 "winnerId":54, 10 "numbers":[52,3,12,11,18,22] 11 }] 12 } 13 }
rest-assured可以非常简单的发起这个GET请求并且验证响应结果,比如:我们想验证 lottoId 是否等于 5 ,可以这样做:
1 get("/lotto").then().body("lotto.lottoId",equalTo(5));
或者我们想验证 winnerId的值是否是23,54 :
1 get("/lotto").then().body("lotto.winners.winnerId",hasItems(23,54));
值得注意的是:equalTo()方法和 hasItems()方法是属于 Hamcrest matchers 的方法,所有我们需要静态导入 org.hamcrest.Matchers 。
注意:"json path" 使用 Groovy‘s GPath 标记法,不要与 Jayway‘s JsonPath 混淆。
2.以BigDecimal形式返回 floats 和 doubles
我们可以对rest-assured和JsonPath进行配置,使之以BigDecimal形式返回json里的数值类型数据,而不是返回 floats 和 doubles,参照下面的json:
1 { 2 3 "price":12.12 4 5 }
默认情况下,我们验证 price的值是否等于12.12时是这样做的:
1 get("/price").then().body("price", is(12.12f));
但是如果我们喜欢的话,我们可以通过JsonConfig 来配置rest-assured使之以BigDecimal形式返回json里的数值类型数据:
1 given(). 2 config(RestAssured.config().jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))). 3 when(). 4 get("/price"). 5 then(). 6 body("price", is(new BigDecimal(12.12));
3.匿名JSON根节点验证
一个json文本并不一定有一个命名好的根节点属性,验证这种类型的json,这里有一个例子:
1 [1, 2, 3]
一个匿名的json根路径要验证的话,可以使用 $ 或者是 空字符串 来验证。例如,我们访问 http://localhost:8080/json ,然后通过rest-assured来验证:
1 //第一种方式,使用 $ 代替根节点 2 get("/json").then().body("$",hasItems(1,2,3)); 3 4 //第二种方式,使用 空字符串 代替根节点 5 get("/json").then().body("",hasItems(1,2,3));
4.XML实例
XML可以使用同样的方式来验证。假设向 http://localhost:8080/greetXML 发送一个POST请求,并且返回下面的xml:
1 <greeting> 2 <firstName>{params("firstName")}</firstName> 3 <lastName>{params("lastName")}</lastName> 4 </greeting>
上面的例子返回了一个基于 firstName 和 lastName 请求参数的greeting节点,我们可以通过rest-assured非常简单展示和验证,比如验证 firstName :
1 given(). 2 parameters("firstName", "John", "lastName", "Doe"). 3 when(). 4 post("/greetXML"). 5 then(). 6 body("greeting.firstName", equalTo("John")).
如果我们想同时验证 firstName 和 lastName ,我们可以这样写:
1 given(). 2 parameters("firstName", "John", "lastName", "Doe"). 3 when(). 4 post("/greetXML"). 5 then(). 6 body("greeting.firstName", equalTo("John")). 7 body("greeting.lastName", equalTo("Doe"));
或者是使用简单的写法:
1 with().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body("greeting.firstName", equalTo("John"), "greeting.lastName", equalTo("Doe"));
5.XML命名空间
为了使响应断言把命名空间考虑在内,我们需要使用 io.restassured.config.XmlConfig 定义一个命名空间。例如,这里有一个叫做 namespace-example 的资源位于 http://localhost:8080,返回下面的XML:
1 <foo xmlns:ns="http://localhost/"> 2 <bar>sudo </bar> 3 <ns:bar>make me a sandwich!</ns:bar> 4 </foo>
我们可以申明 http://localhost:8080 这个uri并且验证响应结果:
1 given(). 2 config(RestAssured.config().xmlConfig(xmlConfig().declareNamespace("test", "http://localhost/"))). 3 when(). 4 get("/namespace-example"). 5 then(). 6 body("foo.bar.text()", equalTo("sudo make me a sandwich!")). 7 body(":foo.:bar.text()", equalTo("sudo ")). 8 body("foo.test:bar.text()", equalTo("make me a sandwich!"));
这个路径的语法遵循的是 Groovy‘s XmlSlurper 语法,需要注意的是一直到2.6.0版本,路径语法都不支持 Groovy‘s XmlSlurper 语法。请看 release notes 查看2.6.0之前的语法。
6.XPath
我们也可以通过使用X-Path 来验证XML响应结果,比如:
1 given().parameters("firstName", "John", "lastName", "Doe").when().post("/greetXML").then().body(hasXPath("/greeting/firstName", containsString("Jo")));
或者:
1 given().parameters("firstName", "John", "lastName", "Doe").post("/greetXML").then().body(hasXPath("/greeting/firstName[text()=‘John‘]"));
如果需要在XPath表达式里面使用命名空间的话,需要在配置中启用这些选项:
1 given(). 2 config(RestAssured.config().xmlConfig(xmlConfig().with().namespaceAware(true))). 3 when(). 4 get("/package-db-xml"). 5 then(). 6 body(hasXPath("/db:package-database", namespaceContext));
namespaceContext
是一个javax.xml.namespace.NamespaceContext 的实例 。
三、高级使用实例(复杂的解析以及验证)
这正是rest-assured的闪光点所在,因为rest-assured实现了Groovy,rest-assured从 Groovy 的API中获得了很大的好处。让我们先从 Groovy 的例子来看:
1 def words = [‘ant‘, ‘buffalo‘, ‘cat‘, ‘dinosaur‘] 2 def wordsWithSizeGreaterThanFour = words.findAll { it.length() > 4 }
第一行代码我们只是简单定义了一个包含字符的list集合,第二行代码非常的有趣。第二行代码中我们通过Groovy闭包调用 findAll 方法来查询list集合中所有长度大于4的字符。这个闭包中有一个叫 it 的内部变量,这个 it 就代表了list集合中的当前元素。这段代码的结果是一个新的list集合:wordsWithSizeGreaterThanFour ,包含 buffalo
and dinosaur。
下面有一些有趣的方法,也可以在 Groovy 集合中使用:
- find ------ 查找第一个匹配闭包断言(closure predicate)的元素
- collect ------封装集合中的每一个元素调用闭包的返回值
- sum -------集合中所有元素之和
- max/min --------返回集合中的最大值或最小值
那么我们在验证XML和Json时,如何应用这些优点呢???
1.XML例子
例如我们有个资源 http://localhost:8080/shopping 返回下面的XML:
1 <shopping> 2 <category type="groceries"> 3 <item>Chocolate</item> 4 <item>Coffee</item> 5 </category> 6 <category type="supplies"> 7 <item>Paper</item> 8 <item quantity="4">Pens</item> 9 </category> 10 <category type="present"> 11 <item when="Aug 10">Kathryn‘s Birthday</item> 12 </category> 13 </shopping>
接下来我们要写一个test方法来验证 类型为 groceries 的category节点是否包含 Chocolate和Coffee两个元素,在rest-assured中我们可以这么写:
1 when(). 2 get("/shopping"). 3 then(). 4 body("shopping.category.find { it.@type == ‘groceries‘ }.item", hasItems("Chocolate", "Coffee"));
那么上面的例子当中发生了什么呢?首先使用XPath方法 shopping.category 获取所有的category的一个list集合,在这个list集合中我们调用 find 方法,返回type属性值等于"groceries"的单个category节点,在这个category节点中我们继续获得了所有item元素。从上面可以看出category节点下不止一个item元素,所以会返回一个list集合,然后我们通过Hamcrest matcher 的 hasitems 方法来验证这个list。这就是上面这个例子整个执行过程!
但是如果我们想获得上面的item,然后又不想使用 Hamcrest matcher 的 hasitems 方法来验证,那么我们可以使用XmlPath:
1 // 获得response,并且以字符串输出 2 String response = get("/shopping").asString(); 3 // 从response中获得groceries,"from"是从XmlPath类中静态导入的 4 List<String> groceries = from(response).getList("shopping.category.find { it.@type == ‘groceries‘ }.item");
如果在response中我们仅仅关心的是 groceries ,我们还可以这样做:
1 // 获得response,并以字符串形式输出 2 List<String> groceries = get("/shopping").path("shopping.category.find { it.@type == ‘groceries‘ }.item");
1.1 深度优先搜索
事实上,前面的例子我们还可以进一步简化一下:
1 when(). 2 get("/shopping"). 3 then(). 4 body("**.find { it.@type == ‘groceries‘ }", hasItems("Chocolate", "Coffee"));
** 是一种在XML文件中做深度搜索的捷径。我们搜索第一个具有type属性值等于"groceries"的节点,注意我们并没有在"item"这个Xml路径结束,原因是在category节点返回item值的list集合时toString() 方法被自动调用了。