Python全字段断言之DeepDiff模块详解

 更新时间:2023年08月21日 11:34:35   作者:shines_m  
这篇文章主要介绍了Python全字段断言之DeepDiff模块详解,Python中也提供了deepdiff库,常用来校验两个对象是否一致,包含3个常用类,DeepDiff,DeepSearch和DeepHash,,需要的朋友可以参考下

一、简介

工作中我们经常要两段代码的区别,或者需要查看接口返回的字段与预期是否一致。

Python中也提供了deepdiff库,常用来校验两个对象是否一致,包含3个常用类,DeepDiff,DeepSearch和DeepHash

其中DeepDiff最常用,可以对字典,可迭代对象,字符串等进行对比,使用递归地查找所有差异。

也可以用来校验多种文件内容的差异,如txt、json、图片等…

DeepDiff库常用来校验两个对象是否一致,并找出其中差异之处。

安装:

pip install deepdiff

二、DeepDiff模块

1.deepdiff常用操作

如果实际请求结果和预期值的json数据都一致,那么会返回{}空字典,否则会返回对比差异的结果,接口测试中我们也可以根据这个特点进行断言。

如果对比结果不同,将会给出下面对应的返回:

  • type_changes:类型改变的key
  • values_changed:值发生变化的key
  • dictionary_item_added:字典key添加
  • dictionary_item_removed:字段key删除

2.代码示例

2.1 对比txt文件

from deepdiff import DeepDiff
# 对比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 对比json

from deepdiff import DeepDiff
# 对比json文件
json1={
    'code': 0,
    "message": "成功",
    "data": {
    "total": 28,
    "id":123
    }
}
json2={
    'code':0,
    "message":"成功",
    "data": {
    "total": 29,
    }
}
print(DeepDiff(json1, json2))

# 输出结果,id移除,total值发生改变
{'dictionary_item_removed': [root['data']['id']], 'values_changed': {"root['data']['total']": {'new_value': 29, 'old_value': 28}}}

2.3 DeepDiff在Pytest框架中的应用

注意,对比的报文必须是字典格式!!!

from deepdiff import DeepDiff
import pytest
import requests
# DeepDiff在Pytest框架中的应用, 注意,对比的报文必须是字典格式!!!
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):
        # 返回字典格式报文
        self.response = requests.get('http://www.httpbin.org.json').json()
    def test_case_01(self):
        print('用例对比结果:')
        print(DeepDiff(self.response, self.expect))
    def test_case_02(self):
        print('用例对比结果:')
        print(DeepDiff(self.response['slideshow']['author'], 'Yours Truly1'))
if __name__ == '__main__':
    pytest.main(['-s'])

输出:
PASSED                          [ 50%]用例对比结果:
{}
PASSED                          [100%]用例对比结果:
 {'values_changed': {'root': {'new_value': 'Yours Truly1', 'old_value': 'Yours Truly'}}}

其实,在实际接口断言中,可能需要校验的字段顺序不一样,又或者有一些字段值不需要,为了解决这类问题,Deepdiff也提供了相信的参数,只需要在比较的时候加入,传入对应参数即可。

  • 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.参数介绍

3.1 展示差异的深度

cutoff_distance_for_pairs: (1 >= float > 0,默认值=0.3)

此参数通常结合ignore_order=true使用,用于结果中展示差异的深度。

值越大,则结果中展示的差异深度越大。

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

默认=False,默认忽略字符串类型的更改。如果ignore_string_type_changes=True,则b"Hello" 与 “Hello”被认为是相同的。

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

默认=False,表示忽略数值类型更改。设置为true时,则认为10和10.0是相同的

PS:此参数仅作用与numbers对象比较,如果拿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支持对比结果选择text视图和tree视图展示。

主要区别在于, tree视图具有遍历对象的功能,可以看到哪些对象与哪些其他对象进行了比较。

虽然视图选项决定了输出的格式,但无论你选择哪种视图,你都可以通过使用pretty()方法得到一个更适合阅读的输出

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)
# 默认为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( ) 方法获得更可读的输出, 无论你使用什么视图来生成结果

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".

大家在比较两个数据对象的时候,更多遇到的场景是:key值不同、key新增、key减少、key值类型改变、结构不同

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())
# 结构不同
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模块

该模支持在对象中搜索对象。

几个重要的参数:

  • use_regexp: 使用正则表达式,默认False。
  • strict_checking:强校验,默认Ture。为True时,它将检查要匹配的对象的类型,因此在搜索 '1234' 时,它将不匹配 int 1234。
  • case_sensitive:为True时,表示大小写敏感。
from deepdiff import DeepSearch
obj = ["long somewhere", "string", 0, "somewhere great!"]
# 使用正则表达式
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)
# 强校验
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]']}
{}

正则表达式这个点的应用场景比较多,当你事先对预期结果的值不能进行100%确定时,可以使用正则匹配实际值进行断言

四、grep模块

grep是DeepSearch提供的一个更好用的方法。

它所接受的参数与DeepSearch完全相同,只是需要你用管道将对象送入它,而不是将它作为参数传递。

它的工作原理和 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模块

该模块可以根据值抽取其Key的路径;反过来根据Key路径提取其值

from deepdiff import extract
obj = {"a": [{'2': 'b'}, 3], "b": [4, 5]}
# root+键名+list下标+键名
path = "root[a][0]['2']"
print(extract(obj, path))

输出:
b

到此这篇关于Python全字段断言之DeepDiff模块详解的文章就介绍到这了,更多相关Python的DeepDiff模块内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python3 实现随机生成一组不重复数并按行写入文件

    Python3 实现随机生成一组不重复数并按行写入文件

    下面小编就为大家分享一篇Python3 实现随机生成一组不重复数并按行写入文件的示例。具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • 浅析Python自带性能强悍的标准库itertools

    浅析Python自带性能强悍的标准库itertools

    itertools是python内置的模块,使用简单且功能强大。这篇文章就主要介绍了通过itertools实现可迭代对象的无限迭代、有限迭代和排列组合。感兴趣的同学可以关注一下
    2021-12-12
  • Python3+Django get/post请求实现教程详解

    Python3+Django get/post请求实现教程详解

    这篇文章主要介绍了Python3+Django get/post请求实现教程详解,需要的朋友可以参考下
    2021-02-02
  • 关于Python文本生成的Beam Search解码问题

    关于Python文本生成的Beam Search解码问题

    这篇文章主要介绍了Python文本生成的Beam Search解码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • python django中8000端口被占用的解决

    python django中8000端口被占用的解决

    今天小编就为大家分享一篇python django中8000端口被占用的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • 详解Pandas之容易让人混淆的行选择和列选择

    详解Pandas之容易让人混淆的行选择和列选择

    这篇文章主要介绍了详解Pandas之容易让人混淆的行选择和列选择,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-07-07
  • 利用Python改正excel表格数据

    利用Python改正excel表格数据

    这篇文章主要介绍了利用Python改正excel表格数据,运用面向过程写的,将每一步都放在了不同的函数中,下文详细过程介绍需要的小伙伴可以参考一下
    2022-06-06
  • python中如何使用函数改变list

    python中如何使用函数改变list

    这篇文章主要介绍了python中如何使用函数改变list,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-09-09
  • python如何将数据输出到文件中

    python如何将数据输出到文件中

    这篇文章主要介绍了python如何将数据输出到文件中问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • django views重定向到带参数的url

    django views重定向到带参数的url

    这篇文章主要介绍了django views重定向到带参数的url,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-03-03

最新评论