学术科研
需要按学术分类法审计测试坏味道时…
当调用方要求学术化、可引用的测试坏味道审查(含正式命名类别与严重度分级)时,对已有测试代码做基于证据的审计并给出框架原生修复方案。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要按学术分类法审计测试坏味道时,用19通用
解决思路
- 只审计已暂存或明确指定的测试;先搜索当前工作区再索要代码,搜索未发现相关测试前不得声称文件缺失。
- 仅当生产代码会改变判定结论时才阅读生产代码。
- 遇到不熟悉的框架 API 时,调用 `test-analysis-extensions` 并阅读对应语言的扩展说明。
- 当调用方要求全部 19 种坏味道、要求引用出处,或代码可能包含高信号集合之外的坏味道时,阅读 [完整目录](references/test-smell-catalog.md);若本文件已能回答的窄问题则不要加载。
- 第一步始终是搜索并阅读已暂存的测试,识别语言、框架、边界与集成标记,即使未给出路径也要先做。
- 阅读测试,以及仅会改变判定结论的生产上下文。
- 对每个候选问题:核实实际执行的检查、选择正式类别、按校准规则校准,然后分配严重度;将已证实的非目录内测试有效性缺陷单独报告,不要改标为坏味道。
- 按“造成虚假信心的风险或脆弱性”优先、其次按维护成本,对已确认发现排序。
- 为每个可操作发现给出符合目标框架的正确替换写法;绝不在其他生态中使用 .NET 术语或 API。
- 按高信号决策表判定:断言行为藏在 `if`/`switch`/分支循环后 → Conditional Test Logic,拆分用例或参数化,但不要仅因运行器存在循环就标记表驱动或参数化测试。
- 测试依赖未声明的文件、网络服务、环境变量或数据库 → Mystery Guest 或 Resource Optimism,使依赖显式且封闭,并用完整目录区分二者;不要仅因集成测试使用了其声明的真实资源就否定它。
- 用固定墙钟 sleep 等待结果 → Sleepy Test,改为带超时地 await 或轮询条件;不要仅因是集成测试就降级。
- 可执行测试没有断言、预期异常标记或 mock 验证 → Unknown Test,断言可观察结果;空函数体应叫 Empty Test 而非 Unknown Test。
- 异步断言/协程被创建但未 await 或 return → 属于严重的非目录内假通过缺陷,单独报告并给出所需的 `await`/`return`;不要强行归入 Unknown Test,因为断言语句确实存在。
- 一个测试覆盖多个不相关的生产行为 → Eager Test,拆分为聚焦行为的测试;但不要不考虑范围就标记刻意的端到端工作流。
- 期望的数值字面量在本地没有含义 → Magic Number Test,为领域值命名或从 setup 推导;但不要标记刚添加三个元素后的 `count == 3`。
- 断言依赖 `ToString`、`repr`、`description` 或非契约的显示格式 → Sensitive Equality,改为断言稳定字段或使用结构化匹配器;但若显式契约就是格式化字符串则不要标记。
- 测试手动管理预期异常流程 → Exception Handling,改用框架的异常断言并检查有意义的细节;不要声称捕获后断言的测试什么都没验证。
- 共享 setup 创建了与接收它的测试无关的状态 → General Fixture,删除无用状态或收窄 fixture,廉价状态排低优先级;不要仅因共享就否定相关的共享 setup。
- 测试被禁用或跳过 → Ignored Test,报告所有跳过,但将有跟踪、有理由的跳过排在无解释跳过之后;不要因理由充分就清除跳过,也不要给两者同等紧迫度。
- 分配发现前应用校准规则:mock 调用验证、快照、裸 pytest `assert`、Pester `Should -Invoke` 和预期异常构造都算断言。
- 字面量或快照断言可能暴露覆盖缺口,但没有单独证据时不构成 Unknown Test 或其他坏味道。
- 统计断言语句数量:单个断言永远不是 Assertion Roulette;仅缺少消息本身不是坏味道。
- 同一方法的测试若覆盖不同行为、边界或状态,不构成 Lazy Test;需要存在冗余的等价路径。
- General Fixture 要求共享生命周期状态;重复的局部构造本身既不是 General Fixture 也不是 Test Code Duplication。
- 除非生产上下文或需求表明字符串仅用于显示,否则把公共 API 返回的字符串视为可观察契约;仅存在插值不构成 Sensitive Equality。
- Magic Number Test 要求存在无法解释的 oracle 值;不要标记角色在本地显而易见且与断言行为无关的普通 setup 数量。
- Go 表驱动子测试、pytest/JUnit/xUnit 参数化、Jest/Vitest `.each`、RSpec 数据表、Pester `-ForEach`、Catch2 `SECTION`/`GENERATE` 本身都不是 Conditional Test Logic。
- Go 的 `if err != nil { t.Fatal(...) }` 是惯用断言流程,不是 Exception Handling。
- 集成标记可使声明的外部资源与多步流程合法化,但不能使固定 sleep 或无断言执行合法化。
- 对集成测试,在判断分支覆盖前追踪 helper 工厂与仓库返回类型;若测试分支依赖真实 helper 永远无法产生的子类型或状态,报告不可达断言路径并说明由此产生的虚假信心,不要止步于给出泛化的 Conditional Test Logic 标签。
- 本地临时文件仍符合 Mystery Guest 的正式定义;封闭式创建与清理只降低严重度,不改变分类。
- 格式化名称不能证明显示文本就是稳定契约;在清除 Sensitive Equality 前,需从生产行为或需求确认。
- 不要仅凭方法名推断坏味道,必须指出证明它的语句或 fixture 关系。
- 不要仅凭测试名推断高严重度的非目录内有效性缺陷;没有生产代码或显式契约证明该测试本应调用另一组件时,名称/函数体不匹配最多只是未排序的观察,不能作为测试静默放过损坏生产行为的证据。
- Catch2 的 `SECTION` 和 `GENERATE` 是运行器控制的用例展开,`REQUIRE` 是真实断言;当这些是唯一可疑构造时,学术坏味道判定为 **clean**,不要因测试本可覆盖更广行为而推翻该判定。
- 校准后若不再有实质性坏味道,明确说明;绝不为填充报告而制造发现。
- 绝不要为 void 或不可 await 的 API 提议 `await`;若生产工作是同步的,删除 sleep 并立即断言。
- 严重度依据已证实的风险而非目录中的固定标签:**High** 指行为损坏时仍可能静默通过、产生非确定性失败或隐藏未执行的断言路径;**Medium** 指使失败含义模糊或将测试耦合到不稳定细节;**Low** 主要指维护债务,如有理由的跳过或过宽的廉价 fixture。
- 说明所分配严重度的理由;当周围测试类型使该模式成为有意为之,则降级或省略该发现。
- 按输入规模调整输出:1 到 3 个文件给出结论和一张紧凑表格(严重度、正式坏味道、证据、风险、修复);更大测试套件增加计数与简短优先级顺序,不要跨仪表盘、散文和计划重复发现。
- 仅在有助于说明修复时展示代码,省略未改动的 setup。
- 仅对看似可疑但实际合理的惯用法添加简短的 **Not findings**。
- 将已证实的非目录内有效性缺陷放在学术坏味道结论之后;对无生产契约的干净输入,不要给可选覆盖观察分配严重度,也不要让其推翻干净结论。
- 不要叙述发现过程或目录加载过程,直接返回审计结果。
- 每个报告的坏味道都必须有正式分类名、精确位置、代码证据、实际风险和具体的框架正确修复。
- 验证:每个发现都由代码支撑而非关键词或方法名;Unknown Test 与 Empty Test 保持区分;每个被禁用的测试仍为 Ignored Test,每个本地文件依赖仍为 Mystery Guest,理由与封闭式清理只改变严重度;报告前已校准框架惯用法与集成边界;干净的测试和可疑但合理的惯用法不会被当作填充内容;干净的框架惯用法不会因命名或缺少生产上下文而被转成高严重度的非目录内发现;修复使用目标框架的 API 并保留被测行为;关于已审查文件、构建或测试运行的说法与实际执行的操作一致。
适用边界 · 注意事项
- 不要用于快速、务实的测试审查(应使用 test-anti-patterns)。
- 不要用于编写或运行测试、框架迁移、覆盖率或断言多样性度量。
- 当调用方只提出本文件已能回答的窄问题时,不要加载完整 19 类目录。