自动化测试救不了你的项目,但能救你的周末
凌晨两点十七分,手机在床头柜上震动。我没有睁眼,先在黑暗里伸手去摸它,心里已经隐约知道是谁。运维在群里发了一条消息:“支付回调那块日志不对劲,你们谁看一下?”紧接着是产品经理:“明天早上要给客户演示,能修吗?”
那一刻我很清楚,这不是一个技术问题,这是一个排班问题。因为我们没有自动化测试,所以每一次改动、每一次上线,本质上都是在赌;赌输了,就得有人半夜爬起来,用人力去补上机器没做的事。而那个“有人”,在相当长的一段时间里,大多数时候是我。
我讲这个故事,不是为了控诉什么。恰恰相反,我是想说明白一件事:自动化测试救不了你的项目,但它能救你的周末。
这两句话听起来矛盾,其实不矛盾。今天我想把这件事掰开揉碎地讲一讲。因为我见过太多团队,把自动化测试当成银弹,以为覆盖率上去了、流水线跑绿了,项目就自动变好了;也见过太多团队,因为这种幻觉破灭,又把自动化测试一棍子打死,说它是“面子工程”“维护负担”。这两种态度,都错过了自动化测试真正能给一个团队带来的东西。
一、“自动化测试能救项目”是个迷人的误会
先说清楚:为什么它救不了项目。
一个软件项目失败,原因通常是什么?我大致归一下类:需求本身是错的,或者一直在变;架构烂到改一处塌三处;团队之间不沟通,前后端互相甩锅;进度被外力压得喘不过气,只能靠加班硬顶;组织里没有人为质量负责,只有人为上线日期负责。
你把这五条里任意一条拎出来,都会发现:它们没有一条是自动化测试能解决的。
需求错了,测试写得再全,也只是精确地证明了“我们实现了一个没人要的东西”。架构烂了,自动化测试甚至可能成为帮凶——因为有了测试,团队更敢在烂地基上继续加层,想着“反正有测试兜着”。沟通断了,测试只能暴露问题,不能代替人坐下来把话说清楚。进度被压,测试会被第一个牺牲掉:“这周先不发版了,测试用例回头补。”至于组织问题,那不是工程问题,那是权力和责任的问题。
所以我说,自动化测试救不了项目。项目是被产品判断、架构决策、协作方式、资源投入这些东西决定生死的。测试是下游,是“验收”,它不生产方向,只检验方向有没有被执行歪。
更值得警惕的是,自动化测试有时候会带来一种虚假的安全感。流水线全绿,大家心里踏实,于是没人再去看那个按钮点下去到底对不对,没人再去问“这个需求真的合理吗”。绿,变成了一种安慰剂。而安慰剂的危险在于,它让你推迟了面对真实问题的时间——等你发现不对劲的时候,往往已经晚了。
二、它救不了的那些东西
我想把“救不了”这部分说得更具体一点,因为空泛地说“测试不是银弹”没有意义。
第一,它救不了混乱的需求。需求一天三变,你今天写的测试,明天就是废纸。你甚至会发现,自动化测试的维护成本在需求高频变动的模块上高得离谱,写的时候花两小时,改的时候花两小时,而这条用例可能一共只跑过三次。这不是测试的错,是需求管理的错。这种情况下,该做的是把需求稳定性提上去,而不是硬写测试。
第二,它救不了腐烂的架构。一个循环依赖纠缠、模块边界模糊的系统,写出来的测试会极其脆弱,到处是 mock,到处是“为了让测试能跑”而做的妥协。你甚至会看到有人为了让测试能注入,把生产代码改成了一种谁也不认识的样子。测试的脆弱,是架构脆弱的一面镜子。你在镜子上擦得再干净,脸还是那张脸。
第三,它救不了错位的协作。如果开发和测试是两个对立的阵营,自动化测试会变成互相甩锅的工具——“你写的用例根本测不到问题”“你实现的东西压根不可测”。工具不会弥合人之间的裂缝,只会把裂缝照得更亮。真正需要的是有人把两边拉到一张桌子前,而不是引入一个新的工具、新的流程、新的指责对象。
第四,它救不了没有工程文化的组织。如果管理层只看上线日期,那么“补测试”永远排在“加功能”后面。自动化测试需要持续投入,而持续投入需要组织真的相信长期主义。这不是一个技术选型能解决的,甚至不是技术负责人能单独解决的。
第五,它也救不了你自己的判断力。测试证明的是“代码符合我写的断言”,不是“这个东西是用户想要的”。两者之间隔着一条鸿沟,自动化测试跨不过去。最危险的一种失败,是所有测试都通过,而产品上线之后没有人用——测试对此一无所知,也毫不关心。
说到这里,可能有人要问:那你写这篇文章干什么?既然它这么没用,我为什么还要推它?
因为上面这些讨论的是“救项目”。而我现在要说的是另一件事:救你的周末。
三、但它确实能救你的周末
让我们把视角从“项目”拉到“人”。
一个没有自动化测试的团队,日常是什么样的?我描述一下,你看熟不熟悉:改动一行代码,需要手动点二十个页面;发版前,两个人花一整天做回归;上线之后一旦线上出问题,第一反应是“这次改了啥来着”;每次重构都心惊胆战,因为不知道会碰坏什么;周末和节假日,总有人要盯着群,随时准备回滚。
这些事的共同点是什么?它们都是重复的、机械的、可以被机器执行的。而重复的、机械的、可以被机器执行的工作,恰恰是最不值得占用人类周末的。
自动化测试在这里的作用,其实很朴素:它把“重复验证”这件事从人的时间表里拿走了。
以前发版前要花一整天回归,现在流水线跑二十分钟,你去看一眼结果,喝杯咖啡;以前改一处怕碰坏十处,现在有测试兜着,你敢动手了,重构不再是一场赌博;以前线上出问题靠猜,现在测试和监控告诉你哪条路径被破坏了;以前周末要值班,现在大多数回归由 CI 自动完成,你只需要在真正出事的时候被叫醒——而这个频率,会显著下降。
请注意,这里没有一句话是“项目因此变好了”。项目可能还是很烂,需求可能还是混乱,架构可能还是一团糟。但你个人的时间被还回来了。你不再需要用自己的睡眠,去填补工程体系的窟窿。
这就是标题那两句话的真正含义:自动化测试不解决项目层面的问题,但它解决“人的消耗”的问题。它把本该由机器承担的重复劳动,还给机器。
我甚至觉得,衡量一个团队的工程成熟度,有一个很朴素的标准:他们的发版夜,是不是还需要有人守在电脑前。如果答案是“是”,那不管他们的架构图画得多漂亮、技术栈多新,这个团队都还在用人力续命。
四、两次发版
我想讲两个具体的场景,对比一下。
第一个场景,是我早些年待过的一个团队。项目是一个内部系统,不大,但业务逻辑绕。没有自动化测试,回归全靠人工,清单是一张电子表格,列了四十几条。每次发版的流程是这样的:周三冻结代码,周四两个人开始手动点,点到周五下午,发现问题,修,再点,点到晚上,发版,然后留一个人值班到深夜。如果周五没点完,周末就要有人来加班。
我印象最深的是有一次,一个同事在周六上午给我打电话,说昨天发的版本把某个报表的口径弄错了,客户周一要看。我们两个人各自在家连上环境,查了一个上午,最后发现是一个很不起眼的空值处理被改动了。改动本身是对的,错的是没有人知道它会影响到那张报表。
那次之后我开始想:如果我们有一组覆盖关键路径的自动化测试,这个空值处理的改动,在提交的那一刻就会被拦下来。我们省下的不只是一个周六上午,还有那种“不知道什么时候会炸”的持续的焦虑。
第二个场景,是我后来参与重构的一个项目。刚开始的时候,团队里没人敢动核心模块,因为谁也不知道改了会怎样。我们没有豪言壮语,只是做了一件很笨的事:先给最核心、最不敢动的那几条业务路径补上端到端的测试,不求覆盖率,只求“改动之后主干不塌”。大概花了三周,补了二十几条用例。
然后事情开始变化。有人第一次在周五下午放心地合了一个重构的 PR;发版前的手工回归从一天缩到了两个小时;值班的轮值表上,深夜告警的次数肉眼可见地少了。项目本身的问题还在——架构还是老样子,需求还是乱——但团队里的人,开始能过一个完整的周末了。
这两个场景是我写这篇文章的全部底气。我不认为自动化测试能拯救一个组织,但我确信它能让一群具体的人,少熬几个夜。
五、怎么让自动化测试真的救到你的周末
既然目标不是“救项目”,那做自动化测试的策略就应该跟着变。很多团队做自动化测试失败,不是因为技术不行,而是因为目标定错了:他们想的是“提高覆盖率”“达到行业标准”,结果写了一堆没人看、跑一次挂三次的用例,最后连自己都不信。
如果目标是“救周末”,那么标准就简单多了:它能不能减少你被叫起来处理问题的次数?
基于这个标准,我有几条很务实的建议。
第一,从最痛的地方开始。不要从“最该测的”开始,要从“最容易炸的”和“最不敢动的”开始。支付、登录、订单状态流转、核心报表——这些地方出一次错,就是一个周末。把这几条路径的自动化测试先补上,收益立竿见影。至于那些一年也不会改一次的配置页面,先放一放,它们不会在周六凌晨给你打电话。
第二,先求稳,再求全。一条天天红的测试,比没有测试更糟糕,因为它会训练团队忽略红色。宁可只有二十条稳定的用例,也不要两百条时灵时不灵的用例。测试的稳定性本身就是一种资产,它决定了团队信不信这套东西。一旦大家觉得“反正它就是会挂”,那这套测试就死了,剩下的只是仪式的空壳。
第三,让失败可诊断。测试失败的时候,报告里要能一眼看出“是哪条路径、哪一步、期望什么、实际什么”。如果每次失败都要人肉去查日志,那这套测试只是把工作时间从深夜挪到了白天,并没有真正省下什么。好测试和坏测试的区别,一半在断言,一半在失败信息。
第四,把测试放进流水线,而不是放在某个人的电脑里。只有自动运行的测试才是自动化测试,手动去点的测试叫“脚本”。CI 要成为唯一的真相来源:合并前必须跑,跑不过不许合。这一条是纪律问题,不是技术问题,而恰恰是它,决定了你能不能真的把周末还给自己。
第五,数据与环境要可复现。很多“测试不稳定”的根因不在测试,而在环境:数据库状态不一致、第三方接口时好时坏、时间依赖没有处理。把这些治理好,测试的稳定性会上一个大台阶。这件事枯燥、不性感,但它是所有稳定自动化的地基。
第六,测试代码也是代码。它需要评审,需要重构,需要删除。过期的用例比没有用例更危险,因为它会给你错误的信心。定期清理,和定期加新用例同样重要。一个健康的测试套件,应该是会“呼吸”的,而不是只增不减的。
第七,别追求“测试一切”。有些东西不适合自动化:一次性的探索、高度依赖人的判断的界面审美、数据本身正在剧烈变动的模块。承认这一点,把精力集中在最值钱的地方,反而更容易成功。追求百分之百覆盖,往往是把资源摊薄到所有地方,结果每一处都不够结实。
六、常见的失败模式
观察下来,自动化测试在团队里失败,通常不是失败在技术上,而是失败在下面这几种模式上。
一种是“运动式建设”。某次线上事故之后,领导一拍桌子,大家连夜补了两百条用例,覆盖率从百分之十冲到百分之六十。三个月后,这些用例有一半是红的,没人管;半年后,整个测试目录被注释掉。问题的根子在于,它是被事件驱动的,而不是被日常习惯支撑的。
一种是“测试给领导看”。所有的图表都很漂亮,覆盖率报表每周更新,但没有人真的在本地跑过测试,也没人真的在意流水线的红绿。这本质上是在生产数据,不是在生产质量。
一种是“全押在端到端”。所有的测试都是打开浏览器、点一遍整个流程。这种测试写起来最像“真实使用”,所以最有吸引力,但它最慢、最脆、最难定位。三五条还好,三五百条就是灾难。正确的做法是分层:大量的单元测试打底,适量的集成测试连接,少量的端到端测试守住主干。
一种是“只加不减”。测试越写越多,运行时间越来越长,没人敢删。结果流水线跑四十分钟,大家开始想办法绕过它。测试套件和代码一样,需要定期偿还技术债。
看明白这些模式,你会发现一个共同点:它们都把自动化测试当成了一个“项目目标”,而不是一个“日常工具”。工具是要被使用的,是要被维护的,是要为人服务的。一旦它变成了指标,它就会开始背离它的初衷。
七、再想一想“救不了项目”这件事
写到这里,我其实想修正一下前面的说法。自动化测试确实“救不了项目”,但它对项目不是完全没有影响——只是这个影响是间接的、缓慢的。
当团队不再把时间耗在手工回归上,他们就有余力去讨论需求、去偿还技术债、去重新审视架构。当重构不再是一场赌博,架构的腐烂就有机会被一点点清理掉。当发版不再是一件需要熬夜的大事,发布节奏就可以变快,反馈就可以变快。
换句话说,自动化测试本身不解决问题,但它释放出解决问题的资源——最重要的是,释放出人的精力和心理空间。一个每周都要通宵的团队,是没有力气去想架构和改进的。他们所有的力气,都用在救火上了。而救火这件事会上瘾,因为它有即时的成就感,还能掩盖长期的无力感。
所以更准确的说法也许是:自动化测试救不了项目,但它是项目能被拯救的前提之一。它把项目从“每天都要靠人肉维持”的状态,拉回到“至少可以正常运转”的状态。至于能不能变好,那是后面的事,取决于人。
这也解释了一个常见的困惑:为什么有些团队测试写得很好,项目还是死了。因为项目死于方向、死于市场、死于组织,而不是死于代码。测试只能保证你走在路上不至于掉进坑里,它不能保证你走的方向是对的。
八、结语:把周末还给自己
我做工程师这么多年,越来越相信一件事:好的工程实践,最终都应该体现在人的生活质量上。
如果一个实践让流程更漂亮、图表更好看、汇报更有词,但大家还是要熬夜、还是要周末值班,那它就没有真正解决问题。自动化测试的价值,不在于它能让你在简历上写“覆盖率百分之八十”,而在于它能让你在周五下班之后,安心地把电脑合上。
它不会让你的需求变得清晰,不会让你的架构自动变好,不会让团队突然学会沟通,也不会让管理层突然重视质量。它不是银弹,它救不了你的项目。
但它能拦住那个在周六上午把你叫醒的空值改动。它能让你在发版前不用再点一整天页面。它能让你在重构的时候,手不抖。它能让你把重复的、机械的、本该由机器完成的工作,交还给机器。
它救不了你的项目,但它能救你的周末。而在一个不完美的、总是有点混乱的行业里,一个完整的周末,已经是很珍贵的东西了。
所以,如果你现在正被深夜的告警和周末的值班折磨,我的建议不是“去学一个测试框架”,也不是“去读一本测试驱动的书”,而是:从下周一开始,先把最容易炸的那条路径写一个测试。就一个。让它跑起来,让它接进流水线,让它替你守住那条线。
你会发现,你守住的不是代码,是你的生活。