Python全字段斷言之DeepDiff模塊詳解
一、簡(jiǎn)介
工作中我們經(jīng)常要兩段代碼的區(qū)別,或者需要查看接口返回的字段與預(yù)期是否一致。
Python中也提供了deepdiff庫,常用來校驗(yàn)兩個(gè)對(duì)象是否一致,包含3個(gè)常用類,DeepDiff,DeepSearch和DeepHash
其中DeepDiff最常用,可以對(duì)字典,可迭代對(duì)象,字符串等進(jìn)行對(duì)比,使用遞歸地查找所有差異。
也可以用來校驗(yàn)多種文件內(nèi)容的差異,如txt、json、圖片等…
DeepDiff庫常用來校驗(yàn)兩個(gè)對(duì)象是否一致,并找出其中差異之處。
安裝:
pip install deepdiff
二、DeepDiff模塊
1.deepdiff常用操作
如果實(shí)際請(qǐng)求結(jié)果和預(yù)期值的json數(shù)據(jù)都一致,那么會(huì)返回{}空字典,否則會(huì)返回對(duì)比差異的結(jié)果,接口測(cè)試中我們也可以根據(jù)這個(gè)特點(diǎn)進(jìn)行斷言。
如果對(duì)比結(jié)果不同,將會(huì)給出下面對(duì)應(yīng)的返回:
- type_changes:類型改變的key
- values_changed:值發(fā)生變化的key
- dictionary_item_added:字典key添加
- dictionary_item_removed:字段key刪除
2.代碼示例
2.1 對(duì)比txt文件
from deepdiff import DeepDiff # 對(duì)比file文件 f1, f2 = open('./data/a.txt', 'r', encoding='utf-8').read(), open('./data/b.txt', 'r', encoding='utf-8').read() print(DeepDiff(f1, f2))
輸出:
{'values_changed': {'root': {'new_value': 'abcd', 'old_value': 'abc'}}}
2.2 對(duì)比json
from deepdiff import DeepDiff # 對(duì)比json文件 json1={ 'code': 0, "message": "成功", "data": { "total": 28, "id":123 } } json2={ 'code':0, "message":"成功", "data": { "total": 29, } } print(DeepDiff(json1, json2))
# 輸出結(jié)果,id移除,total值發(fā)生改變
{'dictionary_item_removed': [root['data']['id']], 'values_changed': {"root['data']['total']": {'new_value': 29, 'old_value': 28}}}
2.3 DeepDiff在Pytest框架中的應(yīng)用
注意,對(duì)比的報(bào)文必須是字典格式?。?!
from deepdiff import DeepDiff import pytest import requests # DeepDiff在Pytest框架中的應(yīng)用, 注意,對(duì)比的報(bào)文必須是字典格式?。?! class TestCase: expect = { 'slideshow': { 'author': 'Yours Truly', 'date': 'date of publication', 'slides': [{ 'title': 'Wake up to WonderWidgets!', 'type': 'all' }, { 'items': ['Why <em>WonderWidgets</em> are great', 'Who<em>buys</em> WonderWidgets'], 'title': 'Overview', 'type': 'all' }], 'title': 'Sample Slide Show' } } def setup(self): # 返回字典格式報(bào)文 self.response = requests.get('http://www.httpbin.org.json').json() def test_case_01(self): print('用例對(duì)比結(jié)果:') print(DeepDiff(self.response, self.expect)) def test_case_02(self): print('用例對(duì)比結(jié)果:') print(DeepDiff(self.response['slideshow']['author'], 'Yours Truly1')) if __name__ == '__main__': pytest.main(['-s'])
輸出:
PASSED [ 50%]用例對(duì)比結(jié)果:
{}
PASSED [100%]用例對(duì)比結(jié)果:
{'values_changed': {'root': {'new_value': 'Yours Truly1', 'old_value': 'Yours Truly'}}}
其實(shí),在實(shí)際接口斷言中,可能需要校驗(yàn)的字段順序不一樣,又或者有一些字段值不需要,為了解決這類問題,Deepdiff也提供了相信的參數(shù),只需要在比較的時(shí)候加入,傳入對(duì)應(yīng)參數(shù)即可。
- ignore order(忽略排序)
- ignore string case(忽略大小寫)
- exclude_paths排除指定的字段
代碼中 使用:
print(DeepDiff(self.response, self.expect,view='tree',ignore_order=True,ignore_string_case=True,exclude_paths= {"root['slideshow']['date']"}))
3.參數(shù)介紹
3.1 展示差異的深度
cutoff_distance_for_pairs: (1 >= float > 0,默認(rèn)值=0.3)
此參數(shù)通常結(jié)合ignore_order=true使用,用于結(jié)果中展示差異的深度。
值越大,則結(jié)果中展示的差異深度越大。
from deepdiff import DeepDiff t1 = [[[1.0]]] t2 = [[[20.0]]] print(DeepDiff(t1, t2, ignore_order=True, cutoff_distance_for_pairs=0.3)) print(DeepDiff(t1, t2, ignore_order=True, cutoff_distance_for_pairs=0.2)) print(DeepDiff(t1, t2, ignore_order=True, cutoff_distance_for_pairs=0.1))
輸出:
{'values_changed': {'root[0][0][0]': {'new_value': 20.0, 'old_value': 1.0}}}
{'values_changed': {'root[0][0]': {'new_value': [20.0], 'old_value': [1.0]}}}
{'values_changed': {'root[0]': {'new_value': [[20.0]], 'old_value': [[1.0]]}}}
3.2 ignore types
1)ignore_string_type_changes
默認(rèn)=False,默認(rèn)忽略字符串類型的更改。如果ignore_string_type_changes=True,則b"Hello" 與 “Hello”被認(rèn)為是相同的。
print(DeepDiff(b'hello', 'hello')) print(DeepDiff(b'hello', 'hello', ignore_string_type_changes=True))
輸出:
{'type_changes': {'root': {'old_type': <class 'bytes'>, 'new_type': <class 'str'>, 'old_value': b'hello', 'new_value': 'hello'}}}
{}
2)ignore_numeric_type_changes
默認(rèn)=False,表示忽略數(shù)值類型更改。設(shè)置為true時(shí),則認(rèn)為10和10.0是相同的
PS:此參數(shù)僅作用與numbers對(duì)象比較,如果拿numers和string比較則不生效
from deepdiff import DeepDiff from decimal import Decimal t1 = Decimal('10.01') t2 = 10.01 print(DeepDiff(t1, t2)) print(DeepDiff(t1, t2, ignore_numeric_type_changes=True))
輸出:
{'type_changes': {'root': {'old_type': <class 'decimal.Decimal'>, 'new_type': <class 'float'>, 'old_value': Decimal('10.01'), 'new_value': 10.01}}}
{}
3.3 view
DeepDiff支持對(duì)比結(jié)果選擇text視圖和tree視圖展示。
主要區(qū)別在于, tree視圖具有遍歷對(duì)象的功能,可以看到哪些對(duì)象與哪些其他對(duì)象進(jìn)行了比較。
雖然視圖選項(xiàng)決定了輸出的格式,但無論你選擇哪種視圖,你都可以通過使用pretty()方法得到一個(gè)更適合閱讀的輸出
t1= {"name": "yanan", "pro": {"sh": "shandong", "city": ["zibo", "weifang"]}} t2 = {"name": "changsha", "pro": {"sh": "shandong", "town": ["taian", "weifang"]}} ddiff = DeepDiff(t1, t2, view='tree') print(ddiff) # 默認(rèn)為text ddiff = DeepDiff(t1, t2, view='text') print(ddiff)
輸出:
{'dictionary_item_added': [<root['pro']['town'] t1:not present, t2:['taian', 'w...]>], 'dictionary_item_removed': [<root['pro']['city'] t1:['zibo', 'we...], t2:not present>], 'values_changed': [<root['name'] t1:'yanan', t2:'changsha'>]}
{'dictionary_item_added': [root['pro']['town']], 'dictionary_item_removed': [root['pro']['city']], 'values_changed': {"root['name']": {'new_value': 'changsha', 'old_value': 'yanan'}}}
3.4 pretty( ) 方法
使用pretty( ) 方法獲得更可讀的輸出, 無論你使用什么視圖來生成結(jié)果
t1= {"name": "yanan", "pro": {"sh": "shandong", "city": ["zibo", "weifang"]}} t2 = {"name": "changsha", "pro": {"sh": "shandong", "town": ["taian", "weifang"]}} print(DeepDiff(t1, t2, view='tree').pretty()) print(DeepDiff(t1, t2, view='text').pretty())
輸出:
Item root['pro']['town'] added to dictionary.
Item root['pro']['city'] removed from dictionary.
Value of root['name'] changed from "yanan" to "changsha".
Item root['pro']['town'] added to dictionary.
Item root['pro']['city'] removed from dictionary.
Value of root['name'] changed from "yanan" to "changsha".
大家在比較兩個(gè)數(shù)據(jù)對(duì)象的時(shí)候,更多遇到的場(chǎng)景是:key值不同、key新增、key減少、key值類型改變、結(jié)構(gòu)不同
t1 = { 'Author': '河馬', 'wechat': 'ZZ666' } t2 = { 'Author': '河馬', 'wechat': 'ZZ666', 'Blog' : 'https://www.hctestedu.com/' } t3 = { 'Author': '河馬', 'wechat': 'ZZ777' } t4 = { 'Author': '河馬', 'wechat': 777 } t5 = [{ 'Author': '河馬', 'wechat': 'ZZ666' }] # Key值不同 print(DeepDiff(t1, t3).pretty()) # Key新增 print(DeepDiff(t1, t2).pretty()) # Key減少 print(DeepDiff(t2, t1).pretty()) # Key值類型改變 print(DeepDiff(t1, t4).pretty()) # 結(jié)構(gòu)不同 print(DeepDiff(t1, t5).pretty()) # Key值相同 result = DeepDiff(t1, t1).pretty() print(DeepDiff(t1, t1).pretty()) assert "" == result
輸出:
Value of root['wechat'] changed from "ZZ666" to "ZZ777".
Item root['Blog'] added to dictionary.
Item root['Blog'] removed from dictionary.
Type of root['wechat'] changed from str to int and value changed from "ZZ666" to 777.
Type of root changed from dict to list and value changed from {'Author': '展昭', 'wechat': 'ZZ666'} to [{'Author': '展昭', 'wechat': 'ZZ666'}].
三、DeepSearch模塊
該模支持在對(duì)象中搜索對(duì)象。
幾個(gè)重要的參數(shù):
- use_regexp: 使用正則表達(dá)式,默認(rèn)False。
- strict_checking:強(qiáng)校驗(yàn),默認(rèn)Ture。為True時(shí),它將檢查要匹配的對(duì)象的類型,因此在搜索 '1234' 時(shí),它將不匹配 int 1234。
- case_sensitive:為True時(shí),表示大小寫敏感。
from deepdiff import DeepSearch obj = ["long somewhere", "string", 0, "somewhere great!"] # 使用正則表達(dá)式 item = "some*" ds = DeepSearch(obj, item, use_regexp=True) print(ds) # 大小寫敏感 item = 'someWhere' ds = DeepSearch(obj, item, case_sensitive=True) print(ds) item = 'some' ds = DeepSearch(obj, item, case_sensitive=True) print(ds) # 強(qiáng)校驗(yàn) item = 0 ds = DeepSearch(obj, item, strict_checking=True) print(ds) item = "0" ds = DeepSearch(obj, item, strict_checking=True) print(ds)
輸出:
{'matched_values': ['root[0]', 'root[3]']}
{}
{'matched_values': ['root[0]', 'root[3]']}
{'matched_values': ['root[2]']}
{}
正則表達(dá)式這個(gè)點(diǎn)的應(yīng)用場(chǎng)景比較多,當(dāng)你事先對(duì)預(yù)期結(jié)果的值不能進(jìn)行100%確定時(shí),可以使用正則匹配實(shí)際值進(jìn)行斷言
四、grep模塊
grep是DeepSearch提供的一個(gè)更好用的方法。
它所接受的參數(shù)與DeepSearch完全相同,只是需要你用管道將對(duì)象送入它,而不是將它作為參數(shù)傳遞。
它的工作原理和 linux shell中的grep一樣
from deepdiff import grep obj = ["long somewhere", "string", 0, "somewhere great!"] item = "somewhere" ds = obj | grep(item) print(ds)
輸出:
{'matched_values': ['root[0]', 'root[3]']}
五、Extract模塊
該模塊可以根據(jù)值抽取其Key的路徑;反過來根據(jù)Key路徑提取其值
from deepdiff import extract obj = {"a": [{'2': 'b'}, 3], "b": [4, 5]} # root+鍵名+list下標(biāo)+鍵名 path = "root[a][0]['2']" print(extract(obj, path))
輸出:
b
到此這篇關(guān)于Python全字段斷言之DeepDiff模塊詳解的文章就介紹到這了,更多相關(guān)Python的DeepDiff模塊內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python函數(shù)中*args和**kwargs來傳遞變長(zhǎng)參數(shù)的用法
這篇文章主要介紹了Python編程中使用*args和**kwargs來傳遞可變參數(shù)的用法,文中舉了變長(zhǎng)參數(shù)的例子,需要的朋友可以參考下2016-01-01深入淺析Python科學(xué)計(jì)算庫Scipy及安裝步驟
這篇文章主要介紹了Python科學(xué)計(jì)算庫—Scipy的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10python實(shí)現(xiàn)大學(xué)人員管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)大學(xué)人員管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10基于python + django + whoosh + jieba 分詞器實(shí)現(xiàn)站內(nèi)檢索功能
這篇文章主要介紹了基于python + django + whoosh + jieba 分詞器實(shí)現(xiàn)站內(nèi)檢索功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08Django自定義User模型、認(rèn)證、權(quán)限控制的操作
這篇文章主要介紹了Django自定義User模型、認(rèn)證、權(quán)限控制的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04Python中shape[0]、shape[1]和shape[-1]分別的意思詳解(附代碼)
剛開始使用python做東西,總是不太理解矩陣、數(shù)組相關(guān)的問題,所以在此記錄shape方面的總結(jié),下面這篇文章主要給大家介紹了關(guān)于Python中shape[0]、shape[1]和shape[-1]分別是什么意思的相關(guān)資料,需要的朋友可以參考下2022-11-11利用selenium爬蟲抓取數(shù)據(jù)的基礎(chǔ)教程
這篇文章主要給大家介紹了關(guān)于如何利用selenium爬蟲抓取數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用selenium具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06