web学习笔记编程技术学习与分享
首页 / 工程实践 / 正文

单元测试的边界与收益

2026 年 09 月 09 日约 3 分钟阅读 工程实践

「测试覆盖率 80%」是个虚荣指标。单元测试的价值取决于你测的是什么的什么。

什么值得测

纯逻辑与业务规则:价格计算、状态流转、校验规则、日期处理。输入输出明确,断言稳定,回归价值最高。

曾经出过 bug 的地方:每个线上问题修复后补一个回归测试,是最诚实的测试用例来源。

对外契约:序列化/反序列化、API 响应结构、消息格式——破坏它们的影响远超单点逻辑。

什么不值得测

  • getter/setter、纯转发的胶水代码:测了只是把实现复述一遍;
  • UI 像素级布局:交给人眼看快照,单元测试得不偿失;
  • 重度依赖外部状态的代码:mock 一大堆之后,测试断言的其实是 mock 本身。这类代码的正确策略是重构它,让可测的部分变多。

一个现实的结构

教科书金字塔(大量单测 + 少量集成 + 极少 E2E)在业务系统里的现实版:

        E2E   ← 关键路径 3-5 条,守住「用户能下单」这类底线
      集成      ← 数据库真实读写、API 层,验证拼装正确
    单元        ← 业务规则、工具函数,跑得最快、数量最多

集成测试的比重通常比教科书高——因为「各模块都对,拼起来错」才是业务 bug 的主产区。

好测试的形状

public function test_expired_coupon_is_rejected(): void
{
    $coupon = new Coupon(discount: 10, expiresAt: new DateTimeImmutable('-1 day'));
    $this->expectException(InvalidCouponException::class);
    $cart->apply($coupon);
}
  • 测试名描述行为,不是方法名;
  • 一个测试一个断言点,失败时立刻知道是什么坏了;
  • 断言结果,不断言实现(mock 调用次数只在必要时用);
  • 跑得快。整套单测超过 30 秒,开发者就会开始跳过它。

何时引入

个人项目、原型期可以不写;一旦进入「多人维护 + 线上运行」状态,为业务规则写测试的回报率是所有工程实践里最高的之一。判断时机很简单:当你改一处逻辑、心里开始担心别处会不会坏的时候,就该补测试了。

测试质量工程实践