2.1 单元测试的特性、价值与原则


本节摘要:单元测试能立的根,是快、稳、自动、原子四条铁律——一旦破例,测试就从资产退化成负债。本节用四组反面事故讲清每条铁律为什么硬性,再补上可读与可维护两条软原则。

四条铁律的保质期

很多人以为单元测试的价值来自框架选得好,其实来自遵守四条铁律。写测试不遵守铁律,测试会在三个月内腐烂成一堆没人敢动的摆设。我们一条一条看,每条都配一个真实的翻车现场。

第一条,。一个函数库里上千个单元用例,若平均每条跑十毫秒,整体也就十几秒;可要是有人把真实 JSON 文件读进单元测试,一条就要几十毫秒,上千条就奔着分钟去。后果是改完代码没人愿意等,测试形同虚设。快的量级是"每次保存后随手能跑",慢下来它就不再是安全网,而是收费闸机。

第二条,稳定。测试结果不该随机器、时间、顺序漂移。一条测试昨天绿今天红,若没动代码却翻红,多半因为混进了真实时间戳、随机数或环境依赖。稳定的反面是"间歇性闪红",它比常红更耗命——因为所有人会学会无视它。

第三条,自动化。单机一键跑,不依赖某个人的环境里恰好装了什么。手工点一遍的测试不在 CI 里,也就不会在提交时拦你,它只是心理安慰。

第四条,原子性。一条用例只验证一个行为点,失败时你会立刻知道是哪一处坏了。把十件事塞进一条测试,红灯亮了你还得做二次侦查。

下面把四条铁律放到一张图里,配上各自的"反面事故"。

02-01-fig01

两条"软"原则,同样别丢

铁律之外还有两条软原则,它们不决定测试能不能跑,但决定测试能不能一直活下去。可读:测试是行为规约,是给未来的自己和同事读的文档,命名和断言都要像读菜谱一样清楚。可维护:被测逻辑一改,测试要能低成本地跟上;写得绕、耦合紧的测试,改一次就要动半页,最后长成没人敢碰的刺。

用一条测试把原则串一遍

下面这条价格计算测试,把快、稳、自动、原子、可读一次占全:纯内存(满足快稳自动),只断言一个点(原子),命名直白(可读)。

def calculate_total(items): return round(sum(item["price"] * item["qty"] for item in items), 2) class TestCalculateTotal: def test_three_items_are_summed(self): got = calculate_total([ {"price": 9.90, "qty": 2}, {"price": 1.10, "qty": 1}, ]) assert got == 20.90

它不连库、不读盘、不看时钟,跑一次在毫秒级,任何一台装了 pytest 的机器都能复现,断言的预期值一眼可核。这就是四条铁律的化身。

铁律之间的联动与取舍

四条铁律不是孤立的口吻,它们经常互相缠绕,理解联动才能不被一条卡死。比如"自动"和"稳"是一对——只要 CI 自动跑,就得保证它不因机器差异闪红,所以稳几乎是为自动服务的;而"原子"和"快"也息息相关——一条测试只验证一个点,跑起来才高效,失败定位也快,原子其实是快的副产品。

取舍上最常见的一道坎:先生成还是先稳定。有人为了快速生成测试,用了大量真实时间戳,跑起来是快的(快 ✔),但换台机器或跨日就红(稳 ✘)。此时正确做法是回源码端注入时钟(见第 5.3 节),而不是在测试里反复绕。另一道坎是"可读 vs 原子":一条测外交互的用例,若为了原子而硬拆,反而会把一个不可分割的真实场景砍碎,让读者看不懂。所以原子针对的是"一个行为点",不是"一次调用",中间的度要靠第 2.4 节的命名与组织来把握。

当新增测试反而拖累整体时

还有一个常被忽略的信号:新增一条测试后,整册运行时间显著上升。这时候要问的不是"这条要不要",而是"它是不是真的单元测试"。若一条测试要读真实文件、连真实库,它名义上被塞进单元目录,却享受不到单元的快和稳——这种"伪单元"会同时拖累快、稳两条铁律。正确的处置是把它挪到集成层(第 3 章),而不是继续堆在单元层里。判断新增测试是否"合格单元"的廉价办法,就是看它跑一次是否仍接近毫秒、是否不看环境脸色。

给铁律配一把"退出杠杆"

铁律不是用来无限坚持的,用"退出杠杆"能帮你在极端情况下做决断:当一条测试长期拖累都快不起来、又明显没有守卫价值,与其死守"自动化必跑",不如承认它折旧了——重构它或删掉它,比让它继续污染套件更健康。这把杠杆的把手就是第 2.5 节的反模式清单:先用那些反模式给可疑测试"体检",确属无价值再退出。四铁律保证的是"合格的测试能被信任",而不是"每一条测试都必须无限存活"。

从原则到价值

遵守四条铁律之后,价值自然浮出:回归防护让重构不再胆战心惊,测试当文档让新人少走弯路,设计反馈逼你注意耦合。反过来,一旦某条铁律破例,无论框架多好,测试都会往"负债"滑。所以面对一条慢、脆、绕的测试,理性做法不是"先留着以后修",而是尽早把它修到合格或删掉——腐坏的测试比没有测试更糟。

一条铁律也能成为价值观分歧

团队里常见一种隐蔽冲突:有人把测试当"质量证明",有人把测试当"开发护栏"。前者倾向于把测试写厚重、把异常场景堆满,追求一种"国王看了放心"的齐全感;后者倾向轻、快、直,追求"我改代码时它真能拦住我"。这两种取向没有对错,但它们会直接改变四条铁律的执行松紧——讲究齐全的人会容忍原子性偶尔破一下,讲究护栏的人连一条慢测试都受不了。

真要落地,建议在开工前就把"铁律优先级"这条对齐写进仓库根目录的约定里,而不是等某条测试越来越胖、越来越慢时才翻脸。多数团队的痛苦不是四铁律太苛刻,而是没人承认"快"和"全"本来就有先后。先让"快"赢,跑动起来再加厚,比先追求齐全、再回头治理腐烂要省力得多。

本节要点回顾

  • 四铁律:快、稳、自动、原子,各对应一种反噬事故。
  • 两软原则:可读、可维护,决定命和爽感。
  • 公式:测试价值 = 拦得住回归 × 被信任 − 维护成本。
  • 破例即负债:慢脆绕的测试比没有更糟,该修该删别硬扛。
  • 落地判据:保存后随手可跑、不看环境、CI 必跑、一条一证。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U