用途
Systematic Testing 是一个系统化测试与调试技能,目标是引导测试驱动开发(TDD)、生成全面的测试套件,并提供系统化的调试框架,确保代码得到充分测试,缺陷被有条理地解决,而不是靠反复试错。
激活触发条件
当出现以下情况时激活该技能:
- 用户实现新功能(自动建议编写测试)
- 测试失败(激活调试框架)
- 用户说“为这个写测试”
- 用户提到“TDD”或“test-driven”
- 用户询问调试或故障排查
- 用户说“这个 bug……”或“错误……”
- 在标记功能完成之前(验证测试覆盖率)
主要能力
1. 测试驱动开发(TDD)
完整的 TDD 工作流通过 Skill 工具调用:dev-workflow:test-driven-development。
调用方式:Skill(skill: "dev-workflow:test-driven-development")
该技能提供完整的 RED-GREEN-REFACTOR 循环、严格约束、反模式与验证清单。
快速 TDD 摘要:
- RED:先写失败的测试
- GREEN:写最少代码使其通过
- REFACTOR:在测试保持通过的前提下清理代码
本技能侧重测试生成策略与系统化调试;TDD 方法论细节请使用专用技能。
2. 测试生成
目标:生成覆盖所有场景的全面测试套件。
测试类别:
- 正常用例(Happy Path):使用有效输入和标准流程测试预期、典型用法。
- 边界用例(Boundary Conditions):测试极限与边界,如极小/极大值、预期应有差异的相等值、需要舍入的小数结果、空集合、零长度字符串。
- 错误用例(Invalid Inputs):测试错误处理,如负值、零值、越界参数、null/nil/None 输入、无效类型。
- 集成用例:测试组件交互,如完整请求-响应流程、令牌验证链、跨组件数据传递。
测试生成模板:
suite "[Component/Function Name]":
setup: # Reset state, create test data
teardown: # Clean up, reset mocks
group "normal operation":
test "should [expected behavior for typical input]"
group "edge cases":
test "should handle [boundary condition]"
group "error handling":
test "should reject [invalid input]":
assert_raises(ExpectedError, invalid_call())
group "integration":
test "should work with [other component]"
3. 系统化调试框架
目标:有条理地解决缺陷,而不是随机试错。
阶段一:根因调查
- 稳定复现缺陷:记录确切步骤与复现率
- 识别症状:可见错误、预期行为与实际行为
- 收集证据:错误消息、堆栈跟踪、日志条目、网络请求、触发输入
- 形成初步假设:陈述假设、支持证据与下一步
阶段二:模式分析
回答以下问题:
- 何时失败?特定输入、用户、时机、操作序列?
- 何时正常?哪些输入成功、哪些用户不受影响、回归发生的时间?
- 最近有什么变更?
git log --since="2 days ago" --oneline
git log -p path/to/file
git bisect # Find exact commit that introduced bug
- 环境因素?本地与生产、平台特定、负载相关?
记录模式:列出 FAILS 与 WORKS 的条件,找出区分因素。
阶段三:假设验证
- 创建隔离缺陷的最小测试用例
- 添加插桩(日志)追踪执行
- 用证据确认或否定假设
- 迭代直到找到根因
阶段四:修复与保护
- 编写复现缺陷的测试:验证修复前失败
- 修复根因:而非仅修复症状
- 验证修复后测试通过
- 为类似边界情况添加回归测试
- 记录根因:是什么、为什么、如何修复
- 带上下文提交:引用问题编号,解释原因
测试覆盖率分析
检查覆盖率:运行项目的测试覆盖率命令(如 --coverage 标志、覆盖率插件或专用工具)。
按以下优先级处理覆盖率缺口:
- 关键业务逻辑(最高优先级)
- 安全敏感代码
- 复杂算法
- 错误处理路径
- 边界情况
最佳实践
TDD:
- 始终先写测试,无例外
- 一次一个测试,实现前不要批量编写
- 尽可能小的步骤,每个循环 5-10 分钟
- 测试行为而非实现,不要测试私有方法
- 保持测试简单,测试应比代码更易理解
测试质量:
- 清晰的测试名称,读起来像文档
- 一致使用 Arrange-Act-Assert 结构
- 每个测试一个断言,便于定位失败
- 测试中无逻辑,只用简单数据
- 测试相互独立,不依赖其他测试
调试:
- 先复现,无法复现就无法修复
- 先理解再修复,不要猜测与检查
- 修复根因,不要只处理症状
- 添加回归测试,防止缺陷重现
- 记录原因,帮助未来的调试者
与 Dev-Workflow 的集成
dev-workflow:test-driven-development:引导式 TDD 工作流,增强的 RED-GREEN-REFACTORdev-workflow:systematic-testing:复杂调试、根因追踪、高级插桩
常见反模式(应避免)
- 实现之后才写测试
- 修改测试以匹配实现
- 测试实现细节而非行为
- 跳过重构阶段
- 测试前做多处修改
- 通过随机改代码来调试
- 提交调试日志代码
纪律机制
三次失败架构门
如果关于某个缺陷的三个假设连续失败,问题不在于下一个假设,而在于架构或你的心智模型。停止调试,反问:
- 系统的实际结构是否如我所想?
- 是否有我未追踪的隐藏交互?
- 我是在解决症状还是原因?
🗣 说:“三个假设已失败。在提出第四个之前,停下来质疑架构。”
向用户呈现这一点,不要再浪费一轮猜测。
多组件插桩模式
当缺陷跨越组件边界(如 前端 → API → Worker → 数据库)时,不要随机加日志。使用分层模板:
对每个边界:
- 记录进入组件的输入(带关联 ID)
- 记录离开组件的输出(同一关联 ID)
- 验证一个组件的输出 == 下一个组件的输入
示例:
[req-abc123] Frontend → API: payload={...}
[req-abc123] API received: payload={...}
[req-abc123] API → Worker: job={...}
[req-abc123] Worker received: job={...}
当各层之间的值出现分歧时,就定位到了缺陷。
伙伴信号:你已偏离轨道
留意用户以下话语,它们表明你在猜测或跳过证据:
- “那没有发生吗?”→ 你假设了一个可能不存在的机制
- “别猜了”→ 你在没有证据的情况下提出了 ≥2 个假设
- “它会显示 X 吗?”→ 用户要求的是你本应提出的验证
- “你为什么这么想?”→ 你陈述了没有依据的结论
听到这些时,立即暂停假设生成,收集证据(日志、代码阅读、测试运行)。
备注
- TDD 初期较慢,但整体更快(更少缺陷、更少调试)
- 好的测试是永不过时的文档
- 调试是侦探工作,不是猜测
- 修复缺陷后始终添加回归测试
- 测试覆盖率是最低标准,不是目标