三层验证的一个人配比:单元、集成与六条端到端

二十三条端到端测试被一次按钮文案改动全部打红,他最后只留六条。三层验证按不可逆性与变更频率分配:214 个单元 6.8 秒、31 个集成 74 秒、6 条端到端 252 秒,全量 332.8 秒,受影响集 107.5 秒。

测试不是越多越好,是每一层刚好够用;一个人的配比由失败代价决定,不由教程决定。

一、二十三条端到端,被一句文案改红

十一月的一个周六上午,他把任务卡上的按钮文案从「添加」改成「新建任务」。半小时后 CI 发来邮件:二十三条端到端测试全部失败,因为它们都用按钮文字定位元素。

以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。

他花了三个小时改选择器,产品逻辑一行没动。当天下午他删掉十七条,只留下六条,并给每一条写清楚「它守的是哪条不能坏的路径」。

端到端测试贵,不是因为跑得慢,是因为它把界面细节当成了契约。 界面天天变,契约不该天天变;这是三层分工要解决的第一个问题。

删之前他做了一次统计:二十三条里,过去一年真正抓到过缺陷的只有四条,其余十九条只在他改界面时红过。删掉之后那四条对应的路径,他各留了一条更粗的端到端,只断言状态与数据。

留下的六条,每条开头都写了一句注释:守哪条路径,失败算几级事故。没有这句话的,一律视为可以删。

二、不套 70/20/10,按两个轴分配

常见建议是单元七成、集成两成、端到端一成。这个比例来自有专职测试的团队,它假设写测试的时间便宜;一个人的时间是 192 小时验证预算里挤出来的,比例必须自己算。

他改用两个轴:失败后果可不可逆,以及这块代码改得频不频繁。两个轴交叉出四种处置,比任何百分比都好用。

情形处置例子
高不可逆 + 高频改动单元必须写,且写成表驱动计费金额、权限判定
高不可逆 + 低频改动一条端到端守住整条链结账、删除账号
低不可逆 + 高频改动单元写主分支即可列表排序、文案拼装
低不可逆 + 低频改动不写,靠发布前手点年度报表导出样式

钱、权限、时间、状态机必须进单元层。 计费的四舍五入、按比例退款、时区边界、订阅状态迁移,这四类错误一旦进了生产就是不可逆的;它们也最容易用几十行断言锁死。

端到端只保留不可逆且跨进程的链路。 一条端到端要启动浏览器、真实数据库和支付替身,四十多秒起步;它适合回答「整条链还通不通」,不适合回答「这个函数对不对」。

变更频率不用猜,翻三个月提交记录。 他把 git log --name-only 按目录汇总,改动次数排前五的模块,单元测试必须跟上;一年只动过两次的模块,再重要也只配一条端到端。

他不设覆盖率门槛。 覆盖率能告诉你哪些行没被执行,不能告诉你哪行错了会更贵;一个 85% 覆盖的计费模块,漏掉的往往是退款与跨月那两条分支。

每个测试都从一个真实缺陷倒推。 先写下「上次这里怎么坏的」,再写断言;写不出真实缺陷场景的测试,多半只是在复述实现,实现一改它就跟着改。

三、三层各写什么

三层不是金字塔,是三种分工。下表的「写不写」一列,是他实际用的判据。

层数量全量耗时覆盖什么何时跑写不写的判据
单元2146.8 秒计算、判断、状态迁移每次保存有分支且结果不可逆
集成3174 秒数据库、事务、替身边界每次提交跨模块且涉及写入
端到端6252 秒五条不可逆路径加一条主线发布前与每日一次失败即事故且跨进程

集成层用四个替身、两个真实只读依赖。 支付、邮件、对象存储、OAuth 走替身;时区库和邮编库保留真实版本,因为它们的边界行为正是要验证的东西。每个用例从种子重建:1 个租户、3 个用户、4 个项目、12 个任务,重置只要 2.4 秒。

集成层最值钱的是事务边界。 写一半失败要能整笔回滚,跨租户写入要被拦住;这两件事单元测试看不见,端到端又要四十秒才跑一次,放在集成层最划算。

单元层写的不是函数,是判断。 按比例退款那条他写了七个用例:整月、半月、跨月、已用折扣、试用期、退款后再次订阅、金额为奇数分。七个用例加起来不到 1.2 秒,却锁住了全年最容易出错的一处算术。

六条端到端对应五条不可逆路径加一条主线。 结账、订阅变更、导出、删除账号、权限变更各一条,再加一条「注册到第一个任务完成」,后者用来确认新用户不会在第一屏就卡住。

端到端守的路径单条耗时失败意味着什么
注册到首个任务新用户主线46 秒新用户进不来
完成一次结账结账44 秒收错钱或收不到钱
升级与降级订阅订阅变更38 秒状态与金额不一致
邀请成员并改权限权限变更39 秒越权看到别人的数据
导出全部数据导出45 秒承诺的数据拿不走
删除账号删除账号40 秒删不干净或误删

端到端不测样式,也不测文案。 选择器一律用 data-testid,断言只落在数据与状态上;这样改按钮文字、换布局都不会打红,测试才守得住它该守的东西。

六条端到端各建独立租户,跑完即删。 共用租户会让测试顺序变成隐式依赖,今天过明天挂;每条自己建一个租户、自己收尾,慢一点但可以任意并行。

四、全量 5 分 33 秒,受影响集 107.5 秒

全量跑一次的成本可以直接算出来,受影响集的价值也就能算出来。

复算:
单元 = 214 个 / 6.8 秒
集成 = 31 个 / 74 秒
端到端 = 6 条 / 252 秒
全量 = 6.8 + 74 + 252 = 332.8 秒 = 5 分 33 秒

受影响集(改一个计费模块)= 63 个单元 + 9 个集成 + 2 条端到端
= 63 × (6.8 ÷ 214) + 9 × (74 ÷ 31) + 2 × (252 ÷ 6)
= 2.0 + 21.5 + 84.0 = 107.5 秒
节省 = 332.8 − 107.5 = 225.3 秒 = 3 分 45 秒
一年 12 次发布 = 12 × 225.3 = 2,703.6 秒 = 45.1 分钟

受影响集靠一张映射表,不靠感觉。 他记下每个源码目录对应哪些测试文件;改 billing/ 就自动带出 63 个单元、9 个集成和结账那两条端到端。映射表是手写的,二十行,比任何自动选择工具都可靠。

提交跑受影响集,发布前跑全量。 受影响集会漏掉跨模块的偶发问题;每天一次全量、每次发布一次全量,两个动作加起来一周不到四十分钟,这是漏测与时间之间他能接受的交换。

端到端跑得慢,就并行到两条。 六条分两组,总时长从 252 秒压到 140 秒左右;再往上加并行,测试数据会互相污染,收益不抵调试成本。

332.8 秒放进 CI 可以接受,本地不行。 他在本地只跑受影响集,两分钟以内能拿到结果;超过两分钟的反馈循环,人就会先去做别的事,测试也就白写了。

45.1 分钟不是重点,反馈时间才是。 一年省下的时间不到一个工作日;真正的收益是每次改动后两分钟内知道坏没坏,而不是等发布前一次性面对一堆红。

配比每季度重算一次。 端到端删到六条之后,他又补过两条、又删过一条;三层数量跟着缺陷分布走,不跟着年初定的比例走。

五、行动清单与复算验收

按顺序做一遍,三层配比就有了自己的数字。

  • 数出当前单元、集成、端到端各有多少个,各跑一次记下总耗时。
  • 列出五条不可逆路径,检查每条是否至少有一条端到端守着。
  • 把端到端的文本选择器全部换成 data-testid,删掉只测样式的断言。
  • 给「钱、权限、时间、状态机」四类逻辑各补三条表驱动单元测试。
  • 写一张源码目录到测试文件的映射表,控制在二十行以内。
  • 用映射表跑一次受影响集,与全量耗时对比,记下差值。
  • 把提交跑受影响集、每日与发布前跑全量写进流水线配置。
  • 一个月后回看:哪条端到端从未失败过,也没有守过真实缺陷,考虑删掉。

验收标准:你能报出三层各自的数量与耗时,并说清六条端到端各守哪条路径;说不清,说明你的测试是按习惯长的,不是按代价配的。

使用边界与实操验收

比例不是目标。 214 / 31 / 6 是这套产品在某个阶段的结果,不是要凑齐的数字;换一个产品,三层的数量会完全不同。

端到端不能替代手工验收。 它只覆盖写下来的六条路径;发布前那 35 分钟的关键路径手点仍然要做,两者互补而不重叠。

替身会漂移。 支付与邮件的替身按文档行为实现,供应商改了语义,替身不会自动跟着变;每季度拿一次真实沙箱对一遍。

慢测试要治,不要忍。 单条集成超过 5 秒就先查数据重建与网络调用;把慢归因于「测试就是这样」,最后一定是整套被跳过。

本文不构成对任何测试框架、覆盖率阈值或发布流程的承诺。 数字全部是教学假设,框架行为以官方文档为准。

关键参考与核验范围

以上链接于 2026-10-26 实测可公开访问;未核验版本、价格与配额。核验日期:2026-10-26。

边界句。 本文只讨论一人产品的测试分层与配比,不讨论测试覆盖率目标、性能测试与安全测试的组织方式。

系列导航

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「Indie」更多文章

  1. 开源项目验收:十项能力、四维评分与三种不该开源的项目
  2. 治理、fork 与退出:17 位贡献者、82% 的提交与一次改协议的代价
  3. 双许可与企业版:九家询价、两家成交与 4,800 美元