JSON断言 环境准备 这里以rest-assured官网的示例进行演示学习
1 2 3 4 5 6 7 8 9 10 11 12 13 { "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 ] }] } }
可以使用WireMock进行响应规则的配置
JsonPath(Groovy’s GPath) 在 Groovy 的官网,虽然并未提及它在 json 中的使用,但实际上只要是树形的层级关系,无论是 json、xml 或者其他格式,就可以使用这种简单的语法帮我们去找到其中的值,rest-assured 也已经帮我们实现支持了 GPath 的断言方式。Groovy Gpath官网说明
根节点.子节点
我们可以使用根节点.(点)子节点的方式一层层的找下去,例如我们需要对lottoId
等于 5 进行断言:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testGPathForNode01 () { given(). when(). log().all(). get("http://127.0.0.1:9090/api/json" ). then(). log().all(). body("lotto.lottoId" , equalTo(5 )); }
如果我们想要断言winners
数组下面的winnerId
,检查23和54是否包含其中,可以如下lotto.winners.winnerId
写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testGPathForNode02 () { given(). when(). log().all(). get("http://127.0.0.1:9090/api/json" ). then(). log().all(). body("lotto.winners.winnerId" , hasItems(54 , 23 )); }
索引取值
如果我们想要取某些相同字段中的某一个,可以使用类似索引的方式获取,例如想要断言 winners
数组下面的 winnerId
的第一个值是否为23,可以使用 lotto.winners.winnerId[0]
,写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test void testGPathFoIndex01 () { given(). when(). log().all().get("http://127.0.0.1:9090/api/json" ). then(). log().all().body("lotto.winners.winnerId[0]" , equalTo(23 )); }
如果我们想要取某些相同字段中的最后一个,可以使用 -1
作为索引,例如断言断言 winners
数组下面的 winnerId
的最后一个的值是否为 54
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test void testGPathFoIndex02 () { given(). when(). log().all(). get("http://127.0.0.1:9090/api/json" ). then(). log().all(). body("lotto.winners.winnerId[-1]" , equalTo(54 )); }
findAll 有时候我们需要获取符合某些条件的结果来进行断言,这里 findAll
可以帮助我们实现,我们可以在 findAll
方法中写筛选条件,例如我们想取 winnerId
的值在大于或等于 30 小于 60 之间的结果进行断言,具体写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test void testGPathFoFindAll () { given(). when(). log().all(). get("http://127.0.0.1:9090/api/json" ). then(). log().all(). body("lotto.winners.findAll{ winners -> winners.winnerId >= 30 && winners.winnerId < 60}.winnerId[0]" , equalTo(54 )); }
find find 的用法与 findAll 基本一致,只是 find 默认取匹配到的第一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test void testGPathFoFind () { given(). when(). log().all(). get("http://127.0.0.1:9090/api/json" ). then(). log().all(). body("lotto.winners.find{ winners -> winners.winnerId >= 30 && winners.winnerId < 60}.winnerId" , equalTo(54 )); } }
XML断言 环境准备 GPath
也支持 XML
格式的断言,这里再以 rest-assured 官方给的一个实例做演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?xml version="1.0" encoding="utf-8"?> <shopping > <category type ="groceries" > <item > <name > Chocolate</name > <price > 10</price > </item > <item > <name > Coffee</name > <price > 20</price > </item > </category > <category type ="supplies" > <item > <name > Paper</name > <price > 5</price > </item > <item quantity ="4" > <name > Pens</name > <price > 15</price > </item > </category > <category type ="present" > <item when ="Aug 10" > <name > Kathryn's Birthday</name > <price > 200</price > </item > </category > </shopping >
XmlPath断言语法 若我们要对第二个 name 的值 Coffee 进行断言,写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Test void testXMLForIndex () { given(). when(). get("http://127.0.0.1:9090/api/xml" ). then(). log().all(). body("shopping.category[0].item[1].name" , equalTo("Coffee" )); }
size() 可以利用 size()
方法来获取对应节点的数量,例如这里要断言 category
的数量:
1 2 3 4 5 6 7 8 9 10 11 12 @Test void testXMLForSize () { given(). when(). get("http://127.0.0.1:9090/api/xml" ). then(). log().all(). body("shopping.category.size()" , equalTo(3 )); }
it.@type、it.price 在 xml中 断言中,可以利用 it. 属性或节点的值来作为筛选条件; 例如这里要获取 type
为 supplies
的 category
下的第一个 item
的 name,
以及获取 price
为 10 的商品名 name
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test void testXMLForIt () { given(). when(). get("/xml" ). then(). log().all(). body("shopping.category.findAll{ it.@type == 'supplies' }.item[0].name" , equalTo("Paper" )). body("shopping.category.item.findAll{ it.price == 10 }.name" , equalTo("Chocolate" )); }
* *.findAll对于xml中有一个特别的语法,**.findAll
,可以直接忽略前面的节点,直接对筛选条件进行匹配,依然获取price
为10的商品名name
,写法如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Test void testXMLForFindAll () { given(). when(). get("/xml" ). then(). log().all(). body("**.findAll{ it.price == 10 }.name" , equalTo("Chocolate" )); }
Json-Schema断言 在实际工作中,对接口返回值进行断言校验,除了常用字段的断言检测以外,还要对其他字段的类型进行检测,原因在于:
返回字段较多,无法保证每个字段都写断言
防止客户端未做 null 值的校验判断,如果因为版本变更或网络等原因造成某个不能接收 null 值的返回字段为 null,就很有可能造成软件的崩溃
某些数值是不能为负的
小数点保留位数,对于股票的交易、医疗数据的分析,小数点的精确度都是有其实际价值的
对返回的字段一个个写断言显然是非常耗时的,这个时候就需要一个模板,可以定义好数据类型和匹配条件,除了关键参数外,其余可直接通过此模板来断言(JsonSchema)
Json-Schema简介 JsonSchema官方文档
基于JSON格式定义JSON数据结构的规范
描述现有数据格式
人类和机器可读
完整的结构和数据验证
Json-Schema模板生成
首先要借助于Json schema tool的网站https://www.jsonschema.net/,将返回json字符串复制到页面左边,然后点击INFER SHCEMA,就会自动转换为schema json文件类型,会将每个地段的返回值类型都设置一个默认类型; 在pattern中也可以写正则进行匹配
点击“设置”按钮会出现各个类型返回值更详细的断言设置,这个就是schema最常用也是最实用的功能,也可以对每种类型的字段最更细化的区间值校验或者断言,例如长度,取值范围等,具体感兴趣的话可以从官网学习深入学习;平常对重要字段的校验我通常会选用其他断言,比如hamcrest断言
选择复制功能,可以将生成的schema模板保存下来与rest-assured结合使用
添加maven依赖,在rest-assured完成支持
使用matchesJsonSchemaInClasspath方法对响应结果进行schema断言
rest-assured结合使用 添加maven依赖,在rest-assured完成支持
1 2 3 4 5 <dependency > <groupId > io.rest-assured</groupId > <artifactId > json-schema-validator</artifactId > <version > ${rest-assured.version}</version > </dependency >
使用matchesJsonSchemaInClasspath方法对响应结果进行schema断言
Rest-Assured其他内建校验方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Test void validateStatus () { response.then().statusCode(200 ); } @Test void validateHeader () { response.then().header("Content-Type" , containsString("json" )); } @Test void validateResponseTime () { response.then().time(lessThan(3000L )); } @Test void validateBody () { response.then().body("owner.login" , equalTo("jinglv" )); }
单元测试的断言 以上都是Rest-Assured的内建的断言机制,我们也可以结合单元测试框架的断言机制进行断言,以下是以Junit5为例
Junit5的断言
assertTrue、assertEquals…
assertAll
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Test void getParseResponse () { JsonPath jsonPath = new JsonPath(response.getBody().asString()); System.out.println("repo ID:" + jsonPath.get("id" )); jsonPath.setRoot("owner" ); System.out.println("owner ID:" + jsonPath.get("id" )); assertTrue(response.getHeader("status" ).contains("OK" )); assertEquals(jsonPath.getInt("id" ), 12013318 ); assertAll("this is a group assert:" , () -> assertTrue(response.getHeader("status" ).contains("OK" )), () -> assertEquals(jsonPath.getInt("id" ), 12013318 )); }