需求场景: 在接口自动化测试中, 需要对接口返回的Json信息格式校验和Json信息内容校验
遇到问题: 一般接口返回的信息都是一些具有复杂嵌套的Json数据
在这种情况下,如果想快速的从接口返回信息中提取到想校验的返回内容是不太容易的
解决思路: 使用objectpath库可以简单方便的从一个json数据中快速的检索数据
一. 安装objectpath库
# 文档地址: http://objectpath.org/ pip install objectpath
二. objectpath库的使用
2.1 假设接口的返回信息为如下内容
res_json = { "code": 0, "msg": "请求成功", "data": { "book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, ], "bicycle": { "color": "red", "price": 19.95, "others": [] } } }
2.2 定义一个解析json数据的函数
from typing import List, Dict from itertools import chain from types import GeneratorType as generator import objectpath def parse_json_by_objectpath(res_json: Dict, expr: str) -> (str, List, int): """ :param res_json: 字典数据 :param expr: objectpath提取表达式, :return: json提取结果 """ tree = objectpath.Tree(res_json) extract_content = tree.execute(expr) if isinstance(extract_content, (generator, chain)): return list(extract_content) else: return extract_content
2.3 常用提取操作
# 提取code的值 print(parse_json_by_objectpath(res_json, "$.code")) # 执行结果: 0 # 提取所有price的值 print(parse_json_by_objectpath(res_json, "$..price")) # 执行结果: [8.95, 12.99, 19.95] # 提取所有book下的price的值 print(parse_json_by_objectpath(res_json, "$.data.book.price")) # 执行结果: [8.95, 12.99] # 提取所有price为8.95的title的值 print(parse_json_by_objectpath(res_json, "$..*[@.price is '8.95'].title")) # 执行结果: ['Sayings of the Century'] # 提取所有title中包含Honour的author print(parse_json_by_objectpath(res_json, "$..*['Honour' in @.title].author")) # 执行结果: ['Evelyn Waugh'] # 提取所有category不为空的author属性和title属性 print(parse_json_by_objectpath(res_json, "$..*[@.category is not null].(author,title)")) # 执行结果: [{'author': 'Nigel Rees', 'title': 'Sayings of the Century'}, {'author': 'Evelyn Waugh', 'title': 'Sword of Honour'}] # 运用正则, 判断提取内容是否以Evelyn开头 print(parse_json_by_objectpath(res_json, "/^Evelyn/ matches $..*['Honour' in @.title].author[0]")) # 执行结果: True # 计算出同时具有title属性和price属性的节点个数 print(parse_json_by_objectpath(res_json, "count($..*[@.title and @.price])")) # 执行结果: 2
2.4 一些注意事项
1) $.* 和 $..* 的区别: $.* 返回的是根节点对象, $..* 返回的是包含根节点在内的所有节点对象
print(parse_json_by_objectpath(res_json, "$.*")) # 执行结果: 返回的就是res_json print(parse_json_by_objectpath(res_json, "$..*")) # 执行结果: 返回的是一个列表,列表中包含了所有的节点对象
2) objectpath中的下列几种值均被定义为逻辑False
print(parse_json_by_objectpath(res_json, "not false")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not False")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not f")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not F")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not null")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not Null")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not nil")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not 0")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not 0.0")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not ''")) # 执行结果: True print(parse_json_by_objectpath(res_json, 'not ""')) # 执行结果: True print(parse_json_by_objectpath(res_json, "not []")) # 执行结果: True print(parse_json_by_objectpath(res_json, "not {}")) # 执行结果: True
3) 在进行条件筛选时,将条件查询内容用引号引起了,可以防止出现一些未知错误
# 根据color属性信息,定位bicycle节点对象, 方式1 print(parse_json_by_objectpath(res_json, "$..*[@.color is red]")) # 执行结果: [{'color': 'red', 'price': 19.95, 'others': []}] # 根据color属性信息,定位bicycle节点对象, 方式2 print(parse_json_by_objectpath(res_json, "$..*[@.color is 'red']")) # 执行结果: [{'color': 'red', 'price': 19.95, 'others': []}]
虽然以上两种方式均能定位到bicycle节点对象, 但建议使用方式2, 这样可以屏蔽掉一些莫名其妙的错误
三. objectpath库在接口自动化测试中的使用场景总结
场景1: 对于任一普通的接口, 需要对接口的http响应码和业务码进行校验
场景2: 对接口返回信息的格式进行校验
场景3: 对于列表类接口, 需要对返回列表中某一节点下返回的数据条数进行校验
场景4: 有这么一类新增接口, 当我们新增数据成功后, 新增接口只给我们返回了新增成功的提示信息
而新增后的具体业务信息,需要通过详情或列表接口来进行查找获得
这种场景下就可以运用objectpath库, 即已知A属性的值去查找B属性的值