Python -- 断言的介绍及使用(2)
Deepdiff 简介:
DeepDiff
是一个用于比较两个数据结构(如字典、列表、字符串等)差异的 Python 库。它可以高效地识别并展示数据之间的增删改变化,适用于 JSON、YAML 等结构化数据的差异分析。
自动化测试中,assert
+ deepdiff
+ pytest
+ allure
就是最强组合!!!
基本语法
diff = DeepDiff(t1, t2, ignore_order=False, **kwargs)
t1
: 第一个要比较的对象(旧/原始对象)。t2
: 第二个要比较的对象(新/目标对象)。ignore_order
: 布尔值。如果为True
,则在比较列表(List)时忽略元素的顺序。这对于比较无序的列表非常有用,但会增加计算成本。**kwargs
: 其他可选参数,用于定制比较行为。
常用参数:
1. ignore_order=True
: 忽略列表顺序。
t1 = [1, 2, 3]
t2 = [3, 2, 1]
diff = DeepDiff(t1, t2, ignore_order=True)
print(diff) # 输出: {} (无差异,因为顺序被忽略)
2. exclude_paths
: 忽略特定路径的比较。
t1 = {"a": 1, "b": {"c": 2, "d": 3}}
t2 = {"a": 1, "b": {"c": 2, "d": 4}}
diff = DeepDiff(t1, t2, exclude_paths=["root['b']['d']"])
print(diff) # 输出: {} (忽略了 d 的变化)
3. exclude_types
: 忽略特定类型的对象。
t1 = {"a": 1, "b": set([1, 2])}
t2 = {"a": 1, "b": set([2, 3])}
diff = DeepDiff(t1, t2, exclude_types={set})
print(diff) # 输出: {} (忽略了 set 类型的比较)
4. significant_digits
: 在比较浮点数时指定有效数字位数。
t1 = {"x": 1.111111}
t2 = {"x": 1.111112}
diff = DeepDiff(t1, t2, significant_digits=5)
print(diff) # 输出: {} (5 位有效数字内无差异)
主要返回类型
DeepDiff
返回一个字典,其键(Key)表示差异的类型,值(Value)是具体的差异详情。
1. values_changed
当字典、列表或对象的值发生改变时出现。
from deepdiff import DeepDiff
t1 = {1: 1, 2: 2, 3: 3}
t2 = {1: 1, 2: 4, 3: 3}
diff = DeepDiff(t1, t2)
print(diff)
# 输出: {'values_changed': {"root[2]": {"new_value": 4, "old_value": 2}}}
2. type_changes
当某个位置的数据类型发生改变时出现。
t1 = {1: "hello", 2: 2}
t2 = {1: "hello", 2: "2"}
diff = DeepDiff(t1, t2)
print(diff)
# 输出: {'type_changes': {"root[2]": {"old_type": int, "new_type": str, "old_value": 2, "new_value": "2"}}}
3. dictionary_item_added
/ dictionary_item_removed
当字典中添加或删除了键值对时出现。
t1 = {1: 1, 2: 2}
t2 = {1: 1, 2: 2, 3: 3}
diff = DeepDiff(t1, t2)
print(diff)
# 输出: {'dictionary_item_added': ["root[3]"]}
4. iterable_item_added
/ iterable_item_removed
当列表、元组等可迭代对象中添加或删除了项目时出现。
t1 = [1, 2, 3]
t2 = [1, 2, 3, 4]
diff = DeepDiff(t1, t2)
print(diff)
# 输出: {'iterable_item_added': [{"new_value": 4, "new_path": "root[3]"}]}
注意:
iterable_item_removed
会显示被删除项的值和路径。
5. unprocessed
某些复杂对象(如自定义类、未处理的类型)可能无法完全比较,相关信息会放在这里。
示例:
通过检查 DeepDiff
对象是否为空的方式判断。(if
None
就是 False
,if
not
None
就是 True
)
- 空时(fasle),表示内容一致。
- 不为空时(true),表示内容有差异。
from deepdiff import DeepDiff
def test_deepdiff_case():
data1 = {"a": 1, "b": [2, 3]}
data2 = {"a": 1, "b": [3, 2]}
diff = DeepDiff(data1, data2)
if diff:
raise AssertionError(f"数据不一致:{diff}")
else:
print("数据一致")
🚀️ api接口的通用判断:
结合 httpx
、allure
,写的api
断言工具类。
import json
import httpx
import allure
from deepdiff import DeepDiff
from typing import Dict, List, Optional, Union
# ------------------------------
# 专为httpx.Response设计的断言工具
# ------------------------------
class HTTPXAssertHelper:
@staticmethod
def validate_response(
response: httpx.Response,
expected_status: int = 200,
expected_schema: Optional[Dict] = None,
ignore_fields: Optional[List[str]] = None,
**kwargs
) -> Dict:
"""
验证httpx响应
:param response: httpx.Response对象
:param expected_status: 预期状态码
:param expected_schema: 预期JSON结构
:param ignore_fields: 忽略比较的字段路径
:param kwargs: 其他DeepDiff参数
:return: 解析后的JSON数据
"""
# 基础检查
with allure.step("断言1:判断Http状态码是否符合预期?"):
assert response.status_code == expected_status, \
f"Http状态码不符: 预期{expected_status}, 实际{response.status_code}"
with allure.step("断言2:判断接口的response响应是否为JSON对象?"):
try:
data = response.json()
except json.JSONDecodeError as e:
raise AssertionError(f"响应不是有效JSON: {e}") from None
# 若提供schema则进行深度比较
if expected_schema:
with allure.step("断言3:深度比较接口的response响应是否符合预期?"):
diff = DeepDiff(
expected_schema,
data,
ignore_order=True,
exclude_paths=ignore_fields or [],
**kwargs
)
if diff:
raise AssertionError(
f"响应数据差异:\n{HTTPXAssertHelper._format_diff(diff)}"
)
return data
@staticmethod
def _format_diff(diff: DeepDiff) -> str:
"""格式化差异报告"""
return json.dumps(
diff.to_dict(),
indent=2,
ensure_ascii=False,
default=str # 处理不可序列化对象
)
评论区