使用 JsonPath 完成接口自動化測試中參數關聯和數據驗證(Python語言)


背景:

  1. 接口自動化測試實現簡單、成本較低、收益較高,越來越受到企業重視
  2. restful風格的api設計大行其道
  3. json成為主流的輕量級數據交換格式

痛點:

  1. 接口關聯
    • 也稱為關聯參數。在應用業務接口中,完成一個業務功能時,有時候一個接口可能不滿足業務的整個流程邏輯,需要多個接口配合使用,簡單的案例如:B接口的成功調用依賴於A接口,需要在A接口的響應數據(response)中拿到需要的字段,在調用B接口的時候,傳遞給B接口作為B接口請求參數,拿到后續響應的響應數據。
    • 接口關聯通常可以使用正則表達式去提取需要的數據,但對於json這種簡潔、清晰層次結構、輕量級的數據交互格式,使用正則未免有點殺雞用牛刀的感覺(是的,因為我不擅長寫正則表達式),我們需要更加簡單、直接的提取json數據的方式。
  2. 數據驗證
    • 這里的數據驗證指的是對響應結果進行數據的校驗
    • 接口自動化測試中,對於簡單的響應結果(json),可以直接和期望結果進行比對,判斷是否完全相等即可。如 json {"status":1,"msg":"登錄成功"}
    • 對於格式較復雜,尤其部分數據存在不確定性、會根據實際情況變化的響應結果,簡單的判斷是否完全相等(斷言)通常會失敗。如: json {"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可以通過點語法來檢索數據,如: shell $.store.book[0].title
  • 也可以使用中括號[]的形式,如 shell $['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)] 所有包含地址的老師信息(jsonpath_rw不支持)
$.lemon.teachers[?(@.age < 20)] 所有年齡小於20的年齡信息(jsonpath_rw不支持)

一:使用jsonpath

安裝jsonpath模塊

pip install jsonpath==0.75

解析

# 1:導入相關模塊
import json
import jsonpath

# 2: 准備json字符串
jsonStr = ''' { "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 } '''

# 3:加載json字符串為json對象
json_obj = json.loads(jsonStr)

# 4:使用jsonpath模塊的jsonpath方法提取信息
# eg1: 提取所有包含addr屬性的老師信息,結果為list類型
results = jsonpath.jsonpath(json_obj,"$..teachers[?(@.addr)]")  
print(results)
# 輸出結果:[{'id': '101', 'name': '華華', 'addr': '湖南長沙', 'age': 25}, {'id': '103', 'name': 'Happy', 'addr': '廣東深圳', 'age': 16}, {'id': '104', 'name': '歪歪', 'addr': '廣東廣州', 'age': 29}]

# eg2:提取所有年齡小於20歲的老師的name,結果為list類型
results2 = jsonpath.jsonpath(json_obj,"$.lemon.teachers[?(@.age < 20)].name")  
print(results2)
# 輸出結果為:['Happy']

二:使用jsonpath_rw

安裝jsonpath_rw模塊

pip install jsonpath-rw

解析

# 1:導入相關模塊
import json
from jsonpath_rw import jsonpath, parse

# 2: 准備json字符串
jsonStr = ''' # 同上(略) '''

# 3:加載為json對象
json_obj = json.loads(jsonStr)

# 4:采用parse創建jsonpath對象(該案例是得到所有的老師name)
jsonpath_expr = parse('$.lemon.teachers[*].name')

# 5:通過jsonPath檢索json后返回匹配的數據,類型是DatumInContext的list
datumInContexts = jsonpath_expr.find(json_obj)
# 采用列表推導式檢索出所有匹配的值
values = [datum.value for datum in datumInContexts]
print(values)
# 輸出結果為:['華華', '韜哥', 'Happy', '歪歪']

# 案例2:提取索引為4的老師的name
jsonpath_expr = parse('$.lemon.teachers[3].name')
datumInContexts = jsonpath_expr.find(json_obj)
print(datumInContexts)
values = [datum.value for datum in datumInContexts]
print(values)
# 結果為:['歪歪']

 

更多jsonpath_rw用法參考:

https://www.cnblogs.com/aoyihuashao/p/8665873.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM