快照测试失败主因是渲染输出未预期变化,包括组件真实变更、非确定性内容、环境差异、副作用未隔离及动态类名;维护核心是让变更可见、可审、可控。

快照测试失效通常不是“突然崩溃”,而是因为渲染输出发生了未预期或未受控的变化,导致新旧快照不匹配。关键不在“会不会失效”,而在于“为什么变”和“该不该更新”。维护快照文件的核心原则是:**让变更可见、可审、可控**。
哪些情况会导致快照测试失败(即“失效”)
所谓“失效”,其实是快照比对不通过,表现为 Jest 报错 Snapshot test failed。常见原因包括:
-
组件逻辑或结构发生真实变更:比如新增一个
<div classname="loading">、调整 props 默认值、修改条件渲染分支——这是最正当的失败,说明快照成功捕获了变化。 <li> <strong>引入非确定性内容</strong>:如 <code>new Date().toISOString()、Math.random()、未 mock 的 API 响应、动态 ID(id={uuid()})等。每次运行生成不同字符串,快照必然不一致。 -
环境或依赖差异:不同版本的 React 渲染器、
@testing-library/react升级后可能改变 DOM 序列化格式(例如属性顺序、空格处理);Node.js 版本升级也可能影响 JSON.stringify 行为。 -
副作用未隔离:组件内调用未 mock 的
fetch、localStorage或第三方 SDK,导致渲染结果受外部状态干扰。 -
样式类名被自动哈希(如 CSS Modules / emotion):若快照包含带哈希的 class(如
class="Button__root___abc123"),每次构建 class 名变动,快照就会“假失败”。 -
只用
npx jest --updateSnapshot(或-u)更新确认过的变更:不要在 CI 中加-u,也不要在本地无意识执行。更新前必须人工核对 diff,确认是预期改动(比如 UI 迭代)而非 bug 或随机值泄漏。 -
对非确定性值做标准化处理:用
jest.useFakeTimers()冻结时间;用jest.mock('uuid', () => () => 'test-uuid')固定 ID;对异步数据统一 mock 返回固定对象。 -
剥离不可控内容再快照:如果快照里混入了时间戳、用户 ID、动态 class 等,改用
expect(container.firstChild).toMatchInlineSnapshot()并手动剔除敏感字段,或用toMatchSnapshot({ snapshotFormat: { printFunctionName: false } })(需自定义序列化器)。 -
把快照文件纳入代码审查流程:提交 PR 时,快照文件(
.snap)的 diff 必须被 reviewer 明确确认。Jest 生成的 Markdown 报告(.md)就是为此设计的——它可读性强,方便快速判断结构是否合理变化。 -
避免跨测试污染:每个
test()应独立 clean up(如清除 mock、重置模块),尤其注意beforeEach中的全局状态设置,否则一个测试的副作用可能悄悄改掉另一个测试的快照。
如何正确维护快照文件
维护不是“删掉重来”,而是有策略地响应变更。重点在预防误更新、保障可审查性:
特别注意 AVA 和 Jest 的快照生命周期差异
如果你用的是 AVA(而非 Jest),要注意其 t.try() 中的快照有特殊行为:被丢弃(discarded)或跳过(skipped)的 t.try() 块,其内部生成的快照在执行 --update-snapshots 时会被自动移除。这是有意设计——“被丢弃的尝试不应产生任何影响”。这意味着你在 AVA 中维护快照时,要更关注测试块的执行状态,而不是单纯看文件是否存在。











