用 pytest 写前后对比测试的核心是:同一输入下显式调用旧函数(如 calculate_v1)和新函数(如 calculate_v2),用 assert old_result == new_result 断言输出一致;需统一参数传递方式、处理浮点/字典/时间等易误报类型,并将测试独立置于 tests/compatibility/ 下,标记为 @pytest.mark.compatibility 以便单独运行。

怎么用 pytest 写前后对比测试
重构后代码行为没变,不能靠“看着差不多”来判断。得让测试自动比对旧逻辑和新逻辑的输出是否一致。核心思路是:同一组输入,跑旧函数和新函数,断言输出完全相等。
常见错误现象:AssertionError 突然冒出来,但你不确定是重构引入了 bug,还是测试本身写错了——这时候如果测试没区分“旧实现”和“新实现”,就失去对比意义。
- 把旧函数(比如
calculate_v1)保留在代码里,不删除,只打上@deprecated注释或加_legacy后缀 - 新函数(如
calculate_v2)放在同一模块或相邻模块,确保可 import - 测试函数里显式调用两者,用
assert old_result == new_result,而不是只测新函数是否“不报错” - 输入数据建议用固定值或小规模 fixture,避免随机性干扰对比(比如别用
datetime.now()作输入)
pytest 参数化时如何避免旧/新函数调用错位
用 @pytest.mark.parametrize 跑多组输入很常见,但容易漏掉一个关键点:旧函数和新函数必须用完全相同的参数顺序、类型、结构调用,否则对比失效。
使用场景:比如你重构了一个解析 JSON 字符串的函数,输入是字符串,输出是 dict。若旧函数接受 str,新函数却悄悄改成接收 bytes,parametrize 传的字符串会直接抛 TypeError,你以为是逻辑错,其实是接口不一致。
- 在测试里统一用
**kwargs或构造相同签名的调用表达式,例如:old_out = parse_json_legacy(text=input_str)和new_out = parse_json(text=input_str) - 别依赖位置参数顺序,尤其当函数参数增多时;优先用关键字传参
- 如果旧函数参数名已不同(比如从
raw改成data),在测试中做一次显式映射,而不是改旧函数——重构测试的前提是“不动旧逻辑”
浮点数、时间戳、字典顺序导致的“假失败”
输出看起来一样,但 assert old == new 报错,大概率是数据类型或精度问题。这不是重构出错,而是对比方式太粗糙。
性能 / 兼容性影响:用 json.dumps(..., sort_keys=True) 或 pytest.approx() 做比较,开销极小,但能避开 90% 的误报。
- 浮点数用
assert new_result == pytest.approx(old_result),别直接== - 字典类输出,先转成
json.dumps(obj, sort_keys=True)再比字符串,避免键顺序差异(Python 3.7+ dict 有序,但旧版或跨环境仍可能不一致) - 时间相关字段(如
"updated_at": "2024-05-20T10:30:00"),要么 mockdatetime,要么在断言前把时间字段 pop 掉再比其余部分 - 注意
None、NaN、空列表[]和空元组()在不同函数里是否被归一化处理
如何组织这类测试不污染主测试套件
对比测试不是临时脚本,它要长期运行,但又不能和功能测试混在一起——否则 CI 里失败了,分不清是功能缺陷还是重构偏差。
容易踩的坑:把对比测试放进 test_calculate.py 里,和正常单元测试共用同一个 pytest 执行命令,结果某天重构引入兼容性 break,所有 PR 都卡在 CI,没人知道该修哪。
- 单独建目录
tests/compatibility/,文件名用test_backwards_compatibility.py - 用
pytest -k compatibility单独跑,或在pyproject.toml里配markers = ["compatibility"],然后加@pytest.mark.compatibility - CI 中可设为非阻塞任务(比如只 warning 不 fail),毕竟它是辅助验证,不是业务契约
- 每次重构完,手动跑一次这个套件;上线前再跑——它不是每提交都必须过,而是“改完立刻验”的快照工具
最麻烦的从来不是写对比逻辑,而是旧函数在哪、它的边界输入有哪些、哪些返回值是文档没写的隐式行为。留好 legacy 函数的最小可运行示例,比写十个 assert 更重要。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











