REST-Assured,接口自动化的 "瑞士军刀"- 断言篇

前言

在上篇文章 <REST-Assured,接口自动化的 "瑞士军刀"- 初识篇 > REST-Assured,接口自动化的 "瑞士军刀"- 初识篇 中有介绍了 REST-Assured 测试框架及基础使用,没有了解的小伙伴可以先去看看,这篇将带着大家一起来学习 REST-Assured 测试框架强大的断言机制。

Hamcrest 与 REST-Assured

Hamcrest 是一款用于编写匹配器对象的框架,可以组合创建灵活的匹配器进行断言。

官方网址:http://hamcrest.org/

可以支持 Java、Python、Ruby、Objective-C 等语言

image.png

类似于 TestNG 单元测试框架或者 Junit 单元测试框架中所提供的 Assert 类,比如 TestNG 提供的断言匹配方法:

Assert.assertTrue();
Assert.assertFalse();
Assert.assertEquals();
...

先举几个在 REST-Assured 中用到的较多 Hamcrest 断言匹配方法:

//equalTo:基于传入对象的 equals 方法匹配方式,如果是数组则比较每个元素是否相等。
assertThat("lotto.lottoId", equalTo(5));
//hasItems:测试集合是否包含指定的多个元素
assertThat("lotto.winners.winnerId", hasItems(23, 54));
//...

更多的用法可以参考官方文档示例。

Hamcrest 相比较上述两款单元测试框架自带的断言匹配而言更加强大、优雅、易读。所以 REST-Assured 官方推荐使用的断言匹配就是 Hamcrest。

需要注意的是 REST-Assured 在使用 Hamcrest 的时候需要静态导入:

import static org.hamcrest.Matchers.*;

准备测试环境

我们拿官方文档中的例子来练习,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]
 }]
}
}

为了方便我们练习,我们可以使用 PostMan 模拟 Mock,定制我们想要的响应数据(Json/XML)

image.png

以 PostMan 模拟接口 JSON 响应为例,REST-Assured 发起请求:

given().
when().
    get("https://013844de-a0b4-426b-b5dc-dcd3a2e5afe5.mock.pstmn.io/json").
then().
    log().all();

控制台的输出的返回响应结果:

HTTP/1.1 200 OK
Date: Fri, 16 Oct 2020 10:08:18 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: nginx
x-srv-trace: v=1;t=7889c71dfff0bf98
x-srv-span: v=1;s=9305333febed8b23
Access-Control-Allow-Origin: *
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1602842942
ETag: W/"2d1-xSYx3nsyyhHEU4lc3Wu1ZhmyStM"
Vary: Accept-Encoding
Content-Encoding: gzip

{
    "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
                ]
            }
        ]
    }
}

JSON 响应断言

需要注意的是 REST-Assured 中的 JSON 响应提取是为 Groovy's GPath 语法(简称 GPath),而不是 Jayway's JsonPath 语法。

那么 GPath 到底是什么?以下来自官网的说明

GPath is a path expression language integrated into Groovy which allows parts of nested structured data to be identified. In this sense, it has similar aims and scope as XPath does for XML. The two main places where you use GPath expressions is when dealing with nested POJOs or when dealing with XML

简单来说 GPath 是一种路径表达式语言,类似于 xpath,GPath 不仅可以应用于 XML,还可以应用于嵌套的层级结构(比如 JSON、HTML)。

Gpath 相比较 Jayway's JsonPath 语法更为简洁

比如:验证 lottoId 为 5

given().
when().
    get("https://013844de-a0b4-426b-b5dc-dcd3a2e5afe5.mock.pstmn.io/json").
then().
    assertThat().body("json.lotto.lottoId",equalTo(5));

比如:验证 winnerId 为 23 和 54

//前面部分代码跟上述一致,省略
assertThat().body("json.lotto.winners.winnerId",hasItems(23,54));

如果想要断言第一个 winnerId 为 32,我们可以通过索引访问:

assertThat().body("json.lotto.winners.winnerId[0]",equalTo(23));

还可以通过 greatThan 断言大于某个值

assertThat().body("json.lotto.winners.winnerId[0]",greaterThan(20));

复杂的 JsonPath 解析和验证:

XML 响应断言

由于 XML 响应获取指定值也是使用 GPath 的语法来描述的,所以和上述的语法基本一致。

我们还是拿官网的示例来练习,有如下 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>

验证第二个 category 节点的第一个 item 值为 Paper

given().
when().
    get("https://013844de-a0b4-426b-b5dc-dcd3a2e5afe5.mock.pstmn.io/xml").
then().
    assertThat().body("shopping.category[1].item[0]",equalTo("Paper"));

XML 还可以根据属性进行条件筛选,比如选出第一个 type 属性值为 groceries 节点的所有 item 文本

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

有时候我们还可以采用简写:

assertThat().body("shopping.category.findAll{it.@type=='groceries'}",hasItems("Chocolate","Coffee"));

此表达式相比较上述的少了 item,原因在于在 category 节点上将会自动执行 toString()方法,将会获取 category 节点的所有子节点的文本值。

还有些时候我们在写路径的时候由于路径过长,整个表达式过于复杂,我们可以采取类似于 xpath 的相对路径写法,官方命名为深度优先搜索算法,其具体用法是以"**"开头,后面加上条件筛选即可。

比如:上面的例子使用深度优先搜索:

assertThat().body("**.find { it.@type == 'groceries' }",hasItems("Chocolate","Coffee"));

通过 find 找到符合筛选条件的第一个对应节点。

1 操作
shakebabe 在 2020-10-19 10:44:29 更新了该帖
3 回帖
请输入回帖内容 ...