背景:

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

RESTFul 风格的 API 设计大行其道

JSON 成为主流的轻量级数据交换格式

痛点:

接口关联

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

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

数据验证

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

接口自动化测试中,对于简单的响应结果(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)
运算符
说明
$
根元素
@
当前元素
*
通配符,可以表示任何元素
..
递归搜索
.
子节点(元素)
['' (, '')]
一个或者多个子节点
[ (, )]
一个或者多个数组下标
[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 依赖

com.jayway.jsonpath
json-path
2.3.0

读取和解析

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

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

java接口验证证书 java接口校验_json

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

java接口验证证书 java接口校验_数据_02

返回值

在 Java 使用 JsonPath 的时候,JsonPath 读取解析后会将返回值自动转型到指定的类型,

所以需要首先明确期望结果是什么类型,否则会出现转换异常

//Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.util.ListList 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);

最终输出结果:

java接口验证证书 java接口校验_数据_03