pytest-snapshot 是一个 pytest 快照测试插件,仅支持对 json 可序列化对象(如 dict、list、str)进行序列化存储与比对,用于验证 api 响应、模板渲染结果等结构化数据是否稳定,不支持 ui 截图、像素比对或视觉回归测试。

pytest-snapshot 是什么,它能测什么?
pytest-snapshot 不是视觉回归测试工具,它不处理图片、像素比对或截图。它只做一件事:序列化 Python 对象(如 dict、list、str、datetime)并保存为 JSON 文件,后续运行时对比当前输出与快照是否一致。常用于 API 响应结构、配置生成结果、数据管道中间态等「可序列化对象」的回归验证。
常见错误现象:SnapshotMismatchError 报错但你其实想测的是网页渲染效果——那得换 playwright + percy 或 pytest-playwright 配合图像比对库。
使用场景包括:
- 后端返回的 JSON 结构是否意外变更(比如字段名拼写、嵌套层级、默认值)
- 模板渲染后生成的字典数据(如 Jinja2 渲染上下文)
- 序列化逻辑(如
dataclass转 dict)是否稳定
怎么安装和写第一个快照测试?
先装依赖:
pip install pytest pytest-snapshot
写一个测试函数,用 snapshots fixture(注意不是 snapshot 单数):
def test_api_response(snapshots):
data = {"user": {"id": 123, "name": "alice"}, "ts": "2024-06-01"}
assert data == snapshots("test_api_response.json")
第一次运行会生成 <strong>snapshots</strong>/test_api_response.json;第二次运行则读取该文件并做深比较。路径默认在测试文件同级的 <strong>snapshots</strong> 目录下。
关键点:
-
snapshots()接收字符串参数,即快照文件名,建议带扩展名(如.json)便于识别 - 传入的对象必须是 JSON 可序列化的;含
datetime、UUID、自定义类会报错,需预处理 - 不支持函数、lambda、open file handle 等不可序列化类型
如何处理动态字段(如时间戳、ID)?
快照里不能容忍每次都不一样的值,否则每次跑都失败。常见做法是预清洗数据:
def test_dynamic_response(snapshots):
resp = get_api_data() # 返回含 "created_at": "2024-06-01T12:34:56Z" 的 dict
cleaned = {**resp, "created_at": "2024-01-01T00:00:00Z"} # 替换为固定值
assert cleaned == snapshots("test_dynamic_response.json")
更健壮的方式是用 pytest-snapshot 提供的 snapshot.with_defaults() 或自定义序列化器,但多数情况直接删/替换字段更直观。
- 删字段:
del resp["request_id"] - 正则替换:
re.sub(r'"id": \d+', '"id": 999', json.dumps(resp))(再 load 回来) - 避免在快照中保留
datetime.now()、uuid.uuid4()等调用结果
CI 环境下更新快照要注意什么?
本地跑 pytest --snapshot-update 可覆盖旧快照,但在 CI 中默认禁止写文件。必须显式允许:
pytest --snapshot-update --snapshot-allow-write
但更安全的做法是:
- 开发阶段本地更新,提交快照文件到 Git(快照是代码的一部分)
- CI 中只运行校验,不加
--snapshot-update - 如果 CI 报
SnapshotMismatchError,说明实际输出变了——要确认这是预期变更,再本地更新并提交快照
容易被忽略的一点:快照文件是纯文本,Git diff 可读,但一旦用了中文或特殊字符,注意文件编码是否为 UTF-8;否则可能本地通过、CI 报编码错误。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











