十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AVA 删除测试后的快照清理:--update-snapshots 与快照报告 Diff 机制深度解析

AVA 删除测试后的快照清理:--update-snapshots 与快照报告 Diff 机制深度解析 AVA 删除测试后的快照清理--update-snapshots 与快照报告 Diff 机制深度解析【免费下载链接】avaNode.js test runner that lets you develop with confidence 项目地址: https://gitcode.com/gh_mirrors/ava/ava本篇技术指南围绕 AVA 快照测试生命周期中的一个高频实战场景展开当你删除一个测试或删除测试中的t.snapshot()断言后遗留的陈旧快照如何处理文章以仓库内 test/snapshot-workflow/snapshots/removing-test.js.md 这份快照报告 Diff 为切入点完整还原“删除测试保留数据”与“--update-snapshots删除对应快照块”两种行为并结合 lib/snapshot-manager.js 源码讲透其底层原理。读完后你将掌握快照报告.md的结构、快照文件.snap的二进制格式以及一套可复制的测试删除维护流程。一、背景AVA 快照测试的双文件模型AVA 支持快照测试通过其 Assertions 接口可以快照任意值。根据官方文档 docs/04-snapshot-testing.md 的说明每份包含快照断言的测试文件会对应生成两个文件test.js.snap存放快照的实际数据后续比较依赖它test.js.md快照报告snapshot report在更新快照时重新生成提交到版本控制后可以直接git diff查看快照的变化。文件存放位置遵循固定规则见 lib/snapshot-manager.js 中的determineSnapshotDir测试位于test或tests目录时快照存放在同级snapshots目录例如test/snapshots/main.js.snap测试位于__tests__目录时快照存放在__snapshots__目录其他情况直接存放在测试文件所在目录若在 AVA 配置中指定了snapshotDirfixedLocation则在该固定目录下按测试文件的相对目录结构镜像存放。快照报告.md由 lib/snapshot-manager.js 的generateReport()生成头部固定为# Snapshot report for test.js The actual snapshot is saved in test.js.snap. Generated by AVA.二、问题场景删除测试后陈旧快照去哪了仓库的test/snapshot-workflow套件专门模拟“编写与维护快照测试过程中可能出现的各种情况”见 test/snapshot-workflow/README.md。其中removing-testfixture 精确构造了“删除一个测试”的场景。fixture 文件 test/snapshot-workflow/fixtures/removing-test/test.js 利用环境变量TEMPLATE控制bar测试是否存在const {default: test} await import(process.env.TEST_AVA_IMPORT_FROM); test(foo, t { t.snapshot({foo: one}); }); if (process.env.TEMPLATE) { test(bar, t { t.snapshot({bar: one}); }); }工作流程分两步模拟真实开发过程模板阶段TEMPLATEtrue执行TEMPLATEtrue npx ava --update-snapshots生成包含foo与bar两个快照块的初始状态test.js.snaptest.js.md变更阶段去掉 TEMPLATE再次运行 AVA此时bar测试已不存在相当于用户删除了一个测试。fixture 的初始快照报告 test/snapshot-workflow/fixtures/removing-test/test.js.md 展示了两个块并存的状态# Snapshot report for test.js The actual snapshot is saved in test.js.snap. Generated by AVA. ## foo Snapshot 1 { foo: one, } ## bar Snapshot 1 { bar: one, }可以看到每个测试标题## foo、## bar对应一个快照块块内以 Snapshot N引用块blockquote作为标签随后是缩进 4 空格的值描述。三、两种行为保留数据 vs 删除快照块套件测试 test/snapshot-workflow/removing-test.js 通过beforeAndAfter宏见 test/snapshot-workflow/helpers/macros.js声明了两个断言场景测试标题CLI 参数期望结果Removing a test retains its data无.md与.snap均保持不变expectChanged: falseWith --update-snapshots, removing a test removes its block[--update-snapshots].md与.snap均发生变化expectChanged: true3.1 不传参删除测试后数据被保留普通运行不带--update-snapshots时AVA 只做比较、不写回快照。宏内部断言after.report before.report、after.snapshot与before.snapshot深度相等——即删除测试不会触发任何文件变更bar的快照数据依然保留在.snap与.md中。这一行为在源码层面的依据是 lib/snapshot-manager.js 的加载逻辑newBlocksByTitle: updating ? new Map() : blocksByTitle,不处于更新模式时newBlocksByTitle直接指向旧的块集合oldBlocksByTitle因此没有增量变化save()会因为hasChanges false直接返回nullsave() 实现两个文件原样保留。3.2 带 --update-snapshots删除测试移除其快照块一旦加上--update-snapshots或简写-unewBlocksByTitle初始化为空 Map。只有本次运行中真正执行了t.snapshot()的测试才会被重新记录进新集合被删除的bar测试不再调用快照断言其块自然被“遗忘”。最终save()用仅含foo的新集合重新编码.snap并重新生成.mdbar块便从两个文件中一起消失。宏在检测到文件有变化时会额外执行一次t.snapshot(cleanStringDiff(before.report, after.report), snapshot report diff)——也就是把“变更前后两份快照报告的文本 diff”本身再做成一个快照其产物正是本篇关联文档 test/snapshot-workflow/snapshots/removing-test.js.md。3.3 快照报告 Diff 的解读cleanStringDifftest/snapshot-workflow/helpers/macros.js使用concordance.diff对比两份报告文本并剥离掉␊换行控制符的可视化表示避免在快照报告中二次转义。文档中removing-test.js.md的核心内容即这个 diff# Snapshot report for test.js The actual snapshot is saved in test.js.snap. Generated by AVA. ## foo Snapshot 1 { foo: one, } - - ## bar - - Snapshot 1 - - { - bar: one, - }带-前缀的行即为被删除的内容## bar标题、 Snapshot 1标签及其值描述被整体移除而foo块保持原样。这正是“--update-snapshots删除测试即删除其快照块”最直观的证据也是将快照报告提交进版本控制的价值所在——每次更新都能通过git diff审查到底哪些快照被增删改。四、底层原理SnapshotManager 的新旧块双 Map 模型深入到 lib/snapshot-manager.jsManager的核心状态是两张按测试标题索引的块集合oldBlocksByTitle本次运行开始前从.snap解码出的既有块newBlocksByTitle本次运行过程中重新记录的快照块。关键流程如下比较compare运行t.snapshot()时compare()L291-L320在newBlocksByTitle中查找同名块与索引取到旧数据后反序列化与本次期望值通过concordance.compareDescriptors做结构化比较决定pass与否记录record / recordSerialized快照断言通过或首次记录时序列化结果写入newBlocksByTitleL322-L339索引严格按序追加或回填越界会抛出RangeError删除的天然实现由于更新模式下新集合从空开始从未被触碰的旧块不会进入新集合——无需任何显式的“删除”操作删除测试的效果由“不重新记录”自然达成保存save见 L385-L418。若处于更新模式且新集合为空所有快照都被删除则通过cleanFile()L475-L484unlinkSync并容忍ENOENT把.snap与.md一并清除否则用write-file-atomic原子写回两个文件块排序sortBlocks新集合按touch()记录的任务索引L287-L289排序保证报告中块的顺序与测试执行顺序一致L164-L183。五、深入.snap 二进制格式与可复现性设计删除测试后.snap同样会被重写理解其格式有助于排查问题。根据 lib/snapshot-manager.js 的encodeSnapshots()与extractCompressedSnapshot()当前版本VERSION 3见 L23-L29的文件布局为可读前缀AVA Snapshot v3\nREADABLE_PREFIX2 字节小端版本号VERSION_HEADER32 字节 SHA-256 校验和gzip 压缩后的 CBOR 编码数据。解码侧decodeSnapshotsL254-L267会重新计算 SHA-256 并与文件内校验和比对不一致抛出ChecksumError版本不匹配抛出VersionMismatchError文件损坏抛出InvalidSnapshotError若检测到 Jest 风格的// Jest Snapshot v1头则抛出LegacyErrorL43-L76。这也解释了为什么.snap无法用文本阅读器直接查看而.md报告是唯一的可读审计入口。为保证跨平台可复现encodeSnapshots()还做了两处关键处理编码前通过normalizeBeforeEncoding剔除值为undefined的属性、按键长优先确定性排序gzip 后强制把 OS 标识字节覆写为 Linuxcompressed[9] 0x03。这样同一份快照在不同机器上生成的内容完全一致fixture 快照才能稳定提交进仓库。六、测试套件如何验证宏、临时目录与串行执行beforeAndAfter宏的完整流程test/snapshot-workflow/helpers/macros.js本身就是一份可参考的“快照变更验证模板”若传入--update-fixture-snapshots先在 fixture 目录内以TEMPLATEtrue执行--update-snapshots重建初始状态readSnapshots()读取变更前的.md文本以及解压后的.snap数据借助extractCompressedSnapshotgunzipSync通过withTemporaryFixture见 test/helpers/with-temporary-fixture.js把 fixture 复制到临时目录再以目标 CLI 参数运行避免污染仓库内 fixture 的初始状态读取变更后的状态按expectChanged断言两个文件是否按预期变化变化时生成 diff 快照。README 中还强调了一条重要约定test/snapshot-workflow/README.md所有使用同一 fixture 的测试必须以相同方式初始化它否则会互相覆盖初始状态而套件大量使用test.serial()主要是为了减轻 CI 上并行启动多个 AVA 进程的负担并非存在共享依赖。七、实战删除测试后的快照维护工作流结合以上机制在实际项目里推荐按如下流程处理“删除测试”先审查再删除由于不更新时.snap/.md保持原样删除测试后运行ava只会在报告中留下“孤儿”块不会报错务必主动检查snapshots目录下的.md是否仍含已删除测试的标题块提交 .md 并审查 diff将快照报告提交到版本控制执行ava --update-snapshots后通过git diff核对被移除的块是否符合预期参考 docs/04-snapshot-testing.md 的“regenerated when you update”说明按需精确更新如果只想更新某个测试可组合使用--update-snapshots与--match或.only()避免一次更新波及全部快照全部删除时的兜底当某个测试文件的最后一个快照被删除时save()会通过cleanFile()同时清理.snap与.md两个文件见 L385-L393无需手工删除目录与路径的注意事项使用自定义snapshotDir时快照会按测试文件的相对目录镜像存放对预编译测试如 TypeScriptresolveSourceFile会借助 source map 定位原始文件L421-L437快照仍落在原始文件旁参见 docs/recipes/typescript.md。八、小结删除测试不是终点清理其遗留快照才是闭环。AVA 通过“旧块/新块双集合 updating标志”的巧妙设计让“删除即遗忘”成为更新模式下的自然结果.snap重编码、.md重新生成、diff 一目了然。本篇关联的 test/snapshot-workflow/snapshots/removing-test.js.md 正是这套行为在 AVA 自身测试套件上的活标本——元快照验证了快照机制本身。若想继续深挖推荐阅读 lib/snapshot-manager.js 的Manager类与 test/snapshot-workflow/removing-test.js、test/snapshot-removal 套件后者还覆盖了快照文件整体删除、跳过快照等更多边界场景。【免费下载链接】avaNode.js test runner that lets you develop with confidence 项目地址: https://gitcode.com/gh_mirrors/ava/ava创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表