侧边栏壁纸
博主头像
一朵云的博客博主等级

拥抱生活,向阳而生。

  • 累计撰写 108 篇文章
  • 累计创建 28 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

Python -- 断言的介绍及使用(2)

一朵云
2023-11-22 / 0 评论 / 0 点赞 / 1511 阅读 / 7784 字

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 就是 Falseif 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("数据一致")

image-rvvf.png

🚀️ api接口的通用判断:

结合 httpxallure,写的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  # 处理不可序列化对象
        )

0

评论区