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

本贴最后更新于 1625 天前,其中的信息可能已经时过境迁

背景:

  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 回帖
请输入回帖内容 ...
  • huahua

    为大佬打call,请问如何在Python里面实现

  • 其他回帖
  • happy

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

  • quanjunfei

    @Happy 老师,有时候遇到,B接口的请求参数,是由A接口的返回接口中,某些字段拼接成的一个值(就是需要对jsonPath返回的数据还要做一些数据处理): "names": { "韬哥" ,"华华","大树"};这种场景如果用excel独立出来,要咋做到和其他一些简单的场景一样做到通用呢? 有没有思路参考参考😀

  • happy

    Python 使用 JsonPath 完成接口自动化测试中参数关联和数据验证
    http://www.lemfix.com/topics/63

  • 查看更多回帖