SuperAPI AI 工具集

gjalla-test-audit

测试与质量

查看项目

更新于 10-14 11:20

技能介绍

Test Audit(测试审计)

对测试套件质量做深度审查,找出会带来虚假信心的测试:它们要么把 bug 固化在断言里,要么重复覆盖,要么被过度 mock 以至于无法捕捉真实回归。

流程

整体上按以下步骤进行,然后把承重代码与测试的 import 做交叉比对。重要性可依据生产影响半径(哪些代码被依赖最多——如果使用 gjalla,影响和变更历史会呈现这些)、近期 bug 历史、以及失败模式的确定性来排序。

阶段 1:定位

深入之前先理解项目的测试基础设施。

  • 绘制测试结构:找出所有测试目录,统计每个目录的文件数,识别命名约定(.test.ts、.pglite.test.ts、.integration.test.ts 等)。
  • 识别测试层次:哪些测试使用真实数据库(PGlite、SQLite)?哪些 mock 了 ORM?哪些在服务边界做 mock?哪些使用 @vitest-environment node 与 jsdom?
  • 识别关键组件:安全边界、数据访问层和核心业务逻辑是什么?这些地方出现虚假信心最危险。

阶段 2:寻找反模式

在各测试层次并行调查。对每个测试文件,同时阅读测试和它声称要测试的源代码。需要找出的反模式:

反模式 1:重实现逻辑的测试

这类测试从不 import 真实代码,而是在测试内联重新定义逻辑并测试自己的副本。信号:

  • 使用 @vitest-environment node 但没有组件/hook import
  • 本地函数命名为 simulate* 或 handle*,与源代码镜像
  • 测试文件对 src/ 或源码目录零 import

这类测试永远不会抓到回归,因为它们没有运行真实代码。

反模式 2:同义反复的 mock 测试

这类测试用硬编码返回值 mock 整条数据库/ORM 链,然后断言这些相同的硬编码值。信号:

  • 对 db.select().from().where() 链使用 mockReturnValue / mockResolvedValue
  • 基于队列的 mock 基础设施(_setSelectQueue、pushSelectResult)
  • 类似 expect(result).toEqual(mockReturnValue) 的断言,而 mockReturnValue 正是 mock 被设定返回的值
  • Builder 模式 mock,其中 .from()、.where()、.innerJoin() 都忽略各自的参数

关键检验:真实代码中的 bug(错表、错列、错 WHERE 子句、错 JOIN)会导致该测试失败吗?如果不会,该测试就是同义反复。

反模式 3:固化错误行为的测试

这类测试的断言验证了不正确行为,而该行为恰好与被测的有 bug 源码一致。信号:

  • 测试夹具使用与源码不匹配的字段名(例如测试用 assignedTier,而源码读 subscriptionTier)
  • 被独立测试的服务之间阈值不一致
  • mock 返回值掩盖了测试声称要验证的逻辑
  • 测试名说一套断言另一套(例如名为 "returns 403" 却断言 toBe(404))

关键检验:测试的 mock 数据与真实上游代码实际产出的数据一致吗?还是它被手工拼凑出来以匹配(可能有 bug 的)被测函数?

反模式 4:冗余的伴随测试

这类测试已被更严格的伴随文件完全覆盖。信号:

  • 一个 mock 数据库的 .test.ts 文件,与一个对同一类做真实 SQL 测试的 .pglite.test.ts 文件并存
  • 一个与 "unit" 测试在同一层次 mock 的 "integration" 测试
  • 针对同一源文件的多个测试文件存在重叠的 describe/it 块

反模式 5:占位测试

  • expect(true).toBe(true)
  • 有描述性名称但没有真实断言
  • 调用 mock 后断言 mock 被调用(同义反复)

阶段 3:归类发现

把发现整理成分层:

  • Tier 1:零真实代码运行的测试(重实现逻辑、内联 mock 处理器)——动作:删除整个文件
  • Tier 2:有用测试与同义反复测试混合的文件——动作:删除同义反复部分,保留逻辑测试
  • Tier 3:有真实伴随(PGlite、integration)的同义反复测试——动作:删除冗余的 mock 版本
  • Tier 4:没有真实伴随的同义反复测试——动作:标记为危险的虚假信心,需要补写真实测试
  • Bugs:固化源码中错误行为的测试——动作:修复源码和测试

阶段 4:报告

以结构化报告呈现发现:

  1. 固化 bug 的测试(最高优先级)——它们掩盖了真实生产问题
  2. Tier 1-3 删除项,含文件路径和行数
  3. Tier 4 缺口——覆盖率会诚实地下降、且需要真实测试的区域
  4. 影响分析:什么变得更稳健,哪些 bug 可能浮现,覆盖率影响

阶段 5:清理(若获批准)

分波执行,每波之间运行测试:

  1. 删除 Tier 1 文件(零真实代码测试)
  2. 移除 Tier 2 同义反复部分
  3. 移除 Tier 3 冗余测试(存在伴随时)
  4. 修复固化错误行为的测试 + 修复源码 bug
  5. 运行覆盖率以识别真实缺口
  6. 为 Tier 4 缺口编写真实测试(对依赖数据库的代码用 PGlite)

原则

  • 不会失败的测试比没有测试更糟。它抬高信心却不提供保护。
  • 在边界 mock,不要 mock 内部。mock 外部服务(Supabase、Stripe、GitHub API)。能用 PGlite 时,不要 mock 自己的数据库层。
  • 往返测试能抓到键不匹配。当函数 A 写入的数据被函数 B 读取时,用 A 的真实输出作为 B 的输入一起测试。手工拼凑的夹具可能悄悄固化 bug。
  • 测试同义反复时,覆盖率数字会说谎。如果 mock 拦截了每个有意义的操作,某一行就不算 "覆盖"。诚实的覆盖率来自运行真实代码路径的测试。
  • 先删除再编写。先移除虚假信心,让覆盖率报告成为你哪里强、哪里暴露的诚实地图。然后填补缺口。