需求場景: 在接口自動化測試中, 需要對接口返回的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屬性的值