使用 JsonPath 完成接口自动化测试中参数关联和数据验证

发布于 更新于
本文目录
  1. 背景:
  2. 痛点:
  3. 解决方案
  4. JsonPath
  5. 运算符(Operators)
  6. JsonPath案例
  7. json
  8. JsonPath例子及说明
  9. JAVA对JsonPath的读取和解析
  10. 在pom.xml中添加Maven依赖
  11. 读取和解析
  12. 返回值

技术文章接口测试

接口自动化测试实现简单、成本较低、收益较高,越来越受到企业重视

背景:

  1. 接口自动化测试实现简单、成本较低、收益较高,越来越受到企业重视

  2. restful风格的api设计大行其道

  3. json成为主流的轻量级数据交换格式

痛点:

  1. 接口关联

  • 也称为关联参数。在应用业务接口中,完成一个业务功能时,有时候一个接口可能不满足业务的整个流程逻辑,需要多个接口配合使用,简单的案例如:B接口的成功调用依赖于A接口,需要在A接口的响应数据(response)中拿到需要的字段,在调用B接口的时候,传递给B接口作为B接口请求参数,拿到后续响应的响应数据。

  • 接口关联通常可以使用正则表达式去提取需要的数据,但对于json这种简洁、清晰层次结构、轻量级的数据交互格式,使用正则未免有点杀鸡用牛刀的感觉(是的,因为我不擅长写正则表达式),我们需要更加简单、直接的提取json数据的方式。

  1. 数据验证

  • 这里的数据验证指的是对响应结果进行数据的校验

  • 接口自动化测试中,对于简单的响应结果(json),可以直接和期望结果进行比对,判断是否完全相等即可。如

   {"status":1,"msg":"登录成功"}
  • 对于格式较复杂,尤其部分数据存在不确定性、会根据实际情况变化的响应结果,简单的判断是否完全相等(断言)通常会失败。如:

{"status":1,"code":"10001","data":[{"id":1,"investId":"1","createTime":"2018-04-27 12:24:01","terms":"1","unfinishedInterest":"1.0","unfinishedPrincipal":"0","repaymentDate":"2018-05-27 12:24:01","actualRepaymentDate":null,"status":"0"},{"id":2,"investId":"1","createTime":"2018-04-27 12:24:01","terms":"2","unfinishedInterest":"1.0","unfinishedPrincipal":"0","repaymentDate":"2018-06-27 12:24:01","actualRepaymentDate":null,"status":"0"},{"id":3,"investId":"1","createTime":"2018-04-27 12:24:01","terms":"3","unfinishedInterest":"1.0","unfinishedPrincipal":"100.00","repaymentDate":"2018-07-27 12:24:01","actualRepaymentDate":null,"status":"0"}],"msg":"获取信息成功"}

上面的json结构嵌套了很多信息,完整的匹配几乎不可能成功。比如其中的createTime信息,根据执行接口测试用例的时间每次都不一样。同时这个时间是响应结果中较为次要的信息,在进行接口自动化测试时,是可以选择被忽略的。

  • 我们需要某种简单的方法,能够从json中提取出我们真正关注的信息(通常也被称为关键信息)。如提取出status的值为1,data数组中每个对象的investId都为1,data中第三个对象的unfinishedPrincipal值为100.00,只要这三个关键信息校验通过,我们就认为响应结果没有问题。

解决方案

JsonPath可以完美解决上面的痛点。通过JsonPath可以从多层嵌套的Json中解析出所需要的值。

JsonPath

  • JsonPath参照XPath解析xml的方式来解析Json

  • JsonPath用符号$表示最外层对象,类似于Xpath中的根元素

  • JsonPath可以通过点语法来检索数据,如:

$.store.book[0].title
  • 也可以使用中括号[]的形式,如

$['store']['book'][0]['title']

运算符(Operators)

运算符

说明

$

根元素

@

当前元素

*

通配符,可以表示任何元素

..

递归搜索

.<name>

子节点(元素)

['<name>' (, '<name>')]

一个或者多个子节点

[<number> (, <number>)]

一个或者多个数组下标

[start:end]

数组片段,区间为[start,end)

[?(<expression>)]

过滤器表达式,其中表达式结果必须是boolean类型,如可以是比较表达式或者逻辑表达式

JsonPath案例

json

{
    "lemon": {
        "teachers": [
            {
                "id": "101",
                "name": "华华",
                "addr": "湖南长沙",
                "age": 25
            },
             {
                "id": "102",
                "name": "韬哥",
                "age": 28
            },
            {
                "id": "103",
                "name": "Happy",
                "addr": "广东深圳",
                "age": 16
            },
             {
                "id": "104",
                "name": "歪歪",
                "addr": "广东广州",
                "age": 29
            }
        ],
        "salesmans": [
            {
                "id": "105",
                "name": "毛毛",
                "age": 17
            },
             {
                "id": "106",
                "name": "大树",
                "age": 27
            }
        ]
    },
 "avg": 25
}

JsonPath例子及说明

JsonPath

路径说明

$.lemon.teachers[*].name

获取所有老师的的名称

$..name

获取所有人的名称

$.lemon.*

所有的老师和销售

$.lemon..age

所有人的年龄

$..age

所有人的年龄

$.lemon.teachers[*].age

所有老师的年龄

$.lemon.teachers[3]

索引为3(第4个)老师的信息

$..teachers[3]

索引为3(第4个)老师的信息

$.lemon.teachers[-2]

倒数第2个老师的信息

$..teachers[-2]

倒数第2个老师的信息

$..teachers[1,2]

第2到第3个老师的信息

$..teachers[:2]

索引0(包含)到索引2(不包含)的老师信息

$..teachers[1:3]

索引1(包含)到索引3(不包含)的老师信息

$..teachers[-2:]

最后的两个老师的信息

$..teachers[2:]

索引2开始的所有老师信息

$..teachers[?(@.addr)]

所有包含地址的老师信息

$.lemon.teachers[?(@.age < 20)]

所有年龄小于20的年龄信息

$..teachers[?(@.age <= $['avg'])]]

小于或等于平均年龄的老师信息

$..teachers[?(@.name =~ /.*PPY/i)]

所有名称满足正则表达式的老师信息 (忽略大小写)

$..*

所有的信息

$.lemon.teachers.length()

老师的数量

JAVA对JsonPath的读取和解析

在pom.xml中添加Maven依赖

<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>2.3.0</version>
</dependency>

读取和解析

最简单直接暴力的方式是使用JsonPath 访问read静态方法:

String json = "...";
List<String> authors = JsonPath.read(json, "$.lemon.teachers[*].name");
image.png

上面的方式每次都会读取并解析这个json字符串,可以采用如下方式读取并解析json字符串一遍:

image.png

返回值

  • 在java使用JsonPath的时候,JsonPath读取解析后会将返回值自动转型到指定的类型, 所以需要首先明确期望结果是什么类型,否则会出现转换异常

//Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.util.List
List<String> list = JsonPath.parse(jsonStr).read("$.lemon.teachers[0].name");

//正常转换
String teacher1 = JsonPath.parse(jsonStr).read("$.lemon.teachers[0].name");
  • 同时jsonpath还提供一些简单对象的映射,如将一个时间戳读取解析为java.util.Date对象

String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
  • 也可以直接将jsonPath映射输出为pojo对象

    • 新建pojo类User

public class User {

   private int id;
   private String name;
   private int age;
       //省略getter、setter方法

映射:

String userJson = "{'id':1,'name':'happy','age':25}";
User user = JsonPath.parse(userJson).read("$",User.class);
System.out.println(user);

最终输出结果: image.png

更多关于JsonPath的知识,请参见: JsonPath

来源:testingpai

继续阅读