为了测试覆盖率而写测试,最后反而成了开发者的负担。

开源爱好者小雨 专家 8小时前 更新于 2026年7月25日 626 浏览 2 点赞 约 2 分钟

在给我的 Candidate Tracker 写 Job Opportunity 创建流程时,我发现了一个很离谱的现象:我的 application-service 测试代码竟然比业务代码本身还难读。

原本的生产环境逻辑极其简单,就三步:
1. 校验并标准化输入数据。
2. 确认所选公司是否存在。
3. 创建职位机会。

结果我的测试代码里充斥着各种 fixture、fakes、spies 和 mocks,甚至还得写一大堆断言来证明某个 repository 方法“绝对没有被调用”。这种复杂度在短期内看着很专业,但实际上它在消耗我的认知带宽。

我花了一下午时间复盘,决定砍掉那些为了测试而测试的代码,只保留真正能给我带来“信心”的用例。

哪些测试是必须留下的?

我最后只保留了三个 application-service 的测试用例,因为它们保护的是核心业务决策:

  • 输入合法 → 数据被正确标准化并创建。
  • 输入非法 → 在持久化之前就被拦截。
  • 公司不存在 → 不会创建孤立的职位记录。

如果这三个用例挂了,意味着系统可能会接受脏数据或者破坏数据库的关系约束,这种风险是不可接受的。

哪些测试被我删掉了?

最让我纠结的是对 repository 返回失败结果的转发测试。理论上,我可以 mock 每一个 repository 方法,然后测试每一个分支,但这样写出来的代码本质上是在验证:

if (!result.success) return result;

这段代码是否真的返回了它收到的结果。这太冗余了,TypeScript 的类型系统已经约束了这一点,而真正的持久化行为应该交给 PostgreSQL 的集成测试去验证。

如果我继续追求 100% 的分支覆盖率,我只会得到一堆毫无意义的 mock 代码,而不会增加我对系统的信心。

总结我的测试分层实操指南

这次踩坑后,我给自己定了一套简单的测试原则,建议大家在构建 AI Agent 或复杂工作流时也可以参考,避免陷入“测试地狱”:

  • 单元测试 (Unit Test): 仅用于业务规则和关键决策点。
  • 集成测试 (Integration Test): 验证实际的持久化行为(比如直接跑在 Docker 数据库上)。
  • 端到端验证 (E2E): 跑通完整的用户工作流。
  • 回归测试 (Regression Test): 只有在真实 Bug 出现且现有测试没覆盖到时,才补写相关用例。
  • 禁忌: 不要为了刷测试数量或代码覆盖率而写测试。

测试的本质应该是降低我们修改代码时的恐惧感。当你发现为了写一个测试而搭建的环境比业务逻辑本身还复杂时,你应该考虑的是简化测试范围,而不是去简化功能。
求助learningbeginnerswebdevtypescript

全部回复 (3)

小李爱学习 初级 11小时前
以前我也这样,为了强行把覆盖率刷到90%以上,结果逻辑改动一点,测试得重写半天。
0 回复
老阿凯 中级 11小时前
后来我把复杂的 mock 全换成轻量级的内存数据库,读起来顺畅多了。
0 回复
架构师老刘 中级 11小时前
这招绝了,之前被mock搞得头大,你用的是哪个库?
0 回复

发表回复

支持 Markdown 格式