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

背景:

  1. 接口自动化测试实现简单、成本较低、收益较高,越来越受到企业重视
  2. RESTFul 风格的 API 设计大行其道
  3. JSON 成为主流的轻量级数据交换格式

痛点:

  1. 接口关联
  1. 数据验证
   {"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 信息,根据执行接口测试用例的时间每次都不一样。同时这个时间是响应结果中较为次要的信息,在进行接口自动化测试时,是可以选择被忽略的。

解决方案

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

JsonPath

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

运算符(Operators)

运算符 说明
$ 根元素
@ 当前元素
* 通配符,可以表示任何元素
.. 递归搜索
. 子节点(元素)
['' (, '')] 一个或者多个子节点
[ (, )] 一个或者多个数组下标
[start:end] 数组片段,区间为[start,end)
[?()] 过滤器表达式,其中表达式结果必须是 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

返回值

//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");
String json = "{\"date_as_long\" : 1411455611975}";
Date date = JsonPath.parse(json).read("$['date_as_long']", Date.class);
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

1 操作
happy 在 2020-07-15 14:29:54 置顶了该帖
9 回帖
请输入回帖内容 ...
  • happy

    如果要先提取数据,然后对数据进行处理,可以尝试函数参数化,如 fun(#{$..names}) ,其中 #{xxx}表示是 jsonpath 提取数据,xxx 表示具体的 jsonpath,fun()表示调用一个函数,通过规则来进行通用处理,通过不同函数进行不同的处理

  • 其他回帖
  • orang_qkbb

    学习了

  • huahua
  • congFly

    大佬,怎么在 python 里面实现

  • 查看更多回帖