pytest断言失败默认不显示diff,需用-vv参数启用结构化比对;仅对内置容器、dataclass等可序列化对象生效,自定义类需实现__dict__或继承dataclass(eq=True)。

断言失败只显示一行 AssertionError,没 diff
这是 pytest 的默认行为——它用的是“简洁模式”,不是 bug。只有显式启用详细比对,才会逐字段标出差异。最直接的开关是加 -vv 参数:pytest -vv test_file.py。注意单个 -v 不够,必须两个 v。
这个机制只对测试函数内的 assert 生效;自己写的 raise AssertionError 完全不走这套逻辑,不会出任何 diff。
- 对象类型要支持:内置容器(
dict、list、tuple)、dataclass、namedtuple才能进结构化 diff - 自定义类默认只显示
<myclass object at></myclass>,得加__repr__或继承dataclass(eq=True) - pytest ≥7.0 对嵌套对象(含
datetime、numpy.array)的 diff 支持更好,老版本容易退化成纯 repr 对比
用了 -vv 还是看不到字段级差异
常见原因是对象类型不被 pytest 原生 diff 引擎识别。比如你写了 class Response,然后 assert r1 == r2 失败,大概率只看到两行地址式输出,没有字段对比。
真进 diff 的路只有一条:让对象可序列化为 dict/list。有三个轻量选择:
立即学习“Python免费学习笔记(深入)”;
- 继承
@dataclass(eq=True),且字段值本身可被 repr(不含不可序列化对象如文件句柄) - 手动实现
__dict__属性,并确保它不含函数、模块、socket 等不可哈希项 - 用
pydantic.BaseModel,但注意别配exclude_unset=True,否则字段缺失会掩盖差异
别碰 __eq__ 里手动 raise,那会让 pytest 直接跳过结构分析,fallback 到 repr。
字符串或嵌套结构太长,diff 被截断成 "+ 123 more"
这不是参数漏了,是 pytest 主动做的截断保护。长字符串、含控制字符或 Unicode 转义的内容,引擎会自动折叠。
更稳的做法不是硬开全局开关,而是把数据“降维”:
- 多行字符串先
.splitlines()转成list再assert,diff 引擎对 list 的逐行标记更准 - 把长变量单独赋值,比如
actual_json = resp.json(),再assert actual_json == expected,pytest 会优先展示变量名对应的内容 - 对含 \uXXXX 的字符串,用
repr(your_str)包一层再 assert,反而更易读
CI 里没 diff,本地却有
大概率是 CI 环境跑测试时没传 -vv,或者用了某些封装脚本/Makefile 把参数吞掉了。检查实际执行命令是否含 -vv 和 --show-capture=all(后者能带出 print() 输出,常被忽略)。
另一个隐蔽点:某些 CI 镜像自带旧版 pytest(比如 6.x),而 -vv 对嵌套对象的 diff 支持在 7.0+ 才完善。用 pytest --version 确认版本,必要时 pip install "pytest>=7.0"。
复杂递归结构(比如带循环引用的 dict)即使满足所有条件,pytest 原生 diff 也可能失效——这时得换 deepdiff,但要注意 ignore_order=True 和 significant_digits=2 这类关键参数,否则看似通过实则漏判。


















