测试覆盖率是一个奇怪的指标。它很容易刷高——写一堆浅层断言就能把数字抬上去;但真正有用的覆盖,是覆盖了最容易出问题的那些路径。
大多数项目测试覆盖率低的原因不是工程师懒,而是:不知道该先写哪里。凭感觉写测试,往往优先覆盖了写起来最简单的代码,而不是最危险的代码。
/tc 要解决的问题是:用图谱数据排优先级,把有限的测试精力集中到最高风险的路径上。
测试覆盖率低的三个根因
在分析了十几个真实项目后,我们发现低覆盖率通常来自三个地方:
- 扇入高但无测试的核心函数:一个被 20 个地方调用的函数,任何一点问题都会扩散到 20 个调用点。但因为它「一直能用」,没有人专门写测试覆盖它的边界条件。
- 最近修改但测试未跟上的代码:功能迭代快,代码改了,测试没更新。这部分代码是最新的,也是最不确定的,但覆盖率反而最低。
- 复杂度高的函数:圈复杂度高意味着分支多,分支多意味着需要更多测试用例才能覆盖到。这类函数的测试往往被拖延,「等重构完再写」。
这三类问题,靠人工梳理识别效率很低。图谱可以直接算出来。
工作流程:扫描 → 排序 → 写测试 → 验证 → 提交
第一步:覆盖率扫描
集成你项目的测试框架(pytest / Jest / JUnit 等),运行一次测试,生成覆盖率报告。/tc 解析报告,得到每个函数的当前覆盖状态。
第二步:图谱排优先级
对每个未覆盖或低覆盖的函数,计算一个优先级分数:
| 因素 | 权重 | 数据来源 |
|---|---|---|
| 扇入(被调用次数) | 40% | 图谱遍历 |
| 最近修改时间 | 30% | git log |
| 圈复杂度 | 20% | 静态分析 |
| 现有测试密度 | 10% | 覆盖率报告 |
输出一个排序列表:从最应该优先覆盖的函数,到最低优先级的。你不需要覆盖所有函数,只需要从列表顶部开始,覆盖到你满意的水平。
第三步:生成测试
对优先级列表里的函数,/tc 生成测试用例。生成测试不是简单的「给这个函数写个测试」,它会:
- 查图谱获取函数的上下游上下文(它被谁调用,调用时的典型参数是什么)
- 分析函数内部的分支路径,确保每个分支都有对应的测试用例
- 对于有副作用的函数(数据库写入、外部 API 调用),生成带 mock 的测试
- 对于纯函数,生成边界条件测试(空输入、极大值、类型边界)
第四步:验证与迭代
生成的测试不保证第一次全部通过。/tc 运行测试套件,对失败的测试做分析:
- 是测试写错了(断言不正确)→ 修复测试
- 是代码有 Bug(被测试发现了)→ 报告给开发者,暂停这个函数的测试生成
- 是 mock 不准确 → 调整 mock 策略,重新生成
最多 3 轮自动修复循环。3 轮后还失败的,标记为「需要人工介入」,继续处理下一个函数。
第五步:提交
测试通过后,提交新增的测试文件。提交触发 manon_impact,确认新增测试没有引入意外的副作用。
真实数据:从 34% 到 79%
在一个中等规模的 Python API 项目(210 个函数,之前测试覆盖率 34%)上的实测结果:
| 指标 | 之前 | 之后 |
|---|---|---|
| 测试覆盖率 | 34% | 79% |
| 新增测试用例数 | — | 87 个 |
| 发现的潜在 Bug | — | 6 个(测试中止,人工确认) |
| 人工编写测试时间 | 约 3 天 | 约 4 小时(人工确认 + 补充) |
那 6 个被发现的潜在 Bug:其中 4 个是边界条件未处理(空列表、None 输入)、2 个是异步竞态问题。这些问题在没有测试覆盖时一直潜伏着,生产环境中低概率出现,很难被发现。
/tc 不做什么
明确边界很重要:
/tc不追求 100% 覆盖率。100% 是噪音,不是目标。目标是覆盖最重要的路径。/tc不替代集成测试。它生成的主要是单元测试和函数级测试,端到端测试需要了解业务流程,超出了自动化的范围。/tc发现 Bug 后不自动修复。修复是开发的责任,测试只是发现问题。
测试是软件质量的保险,保险的价值在于它覆盖了你最在意的风险,而不是覆盖了所有可能性。/tc 帮你找到最值得保的那些地方。