首页 / m.itcdj.com / 正文

写够单元测试才敢改老代码

上周凌晨两点被前同事的电话炸醒。

他说电商大促的满减券又算错了,财务对账差了十二万。

我爬起来登VPN看代码的时候差点吐了——那个满减计算的函数,是2018年写的,四千多行,没有一行测试。

没人敢动。

上次有人加了个新人专属券的逻辑,改了三行,直接把老用户的满减金额乘了二,赔了快十万。

说穿了,大家怕的不是写代码,是改完不知道哪会炸。

没人想写的单元测试,到底是给谁写的

很多人提到单元测试就头疼,觉得是额外的工作量,是给QA写的,是给领导看覆盖率指标的。

真的离谱。

之前见过的团队为了凑够80%的行覆盖率,把实体类的getter、setter全写了测试用例,跑一次全量测试要四十分钟,大家嫌慢,最后提交代码的时候全跳过测试环节,直接推生产。

说穿了,单元测试本质上是给三个月后的自己写的。你现在写的满减逻辑,过三个月你自己都记不清边界——新人券能不能和老用户满减叠加、过期券触发的时候要不要给前端返回特定错误码、实付金额刚好为零的时候要不要扣减库存,这些细节你不写下来,下次改代码的时候全靠拍脑袋猜。

IDEA中JUnit测试用例运行通过界面截图

当然也不是说单元测试要追求完美。没必要测你传个字符串进去函数会不会抛错——那是静态类型检查该干的事,你要盯紧的是真实业务里会踩的坑:满减门槛刚好差一分钱、用户同时有三张可叠加的券、券的有效期刚好在当前时间的前一秒。这些场景你写了测试,下次改逻辑的时候跑一遍,就知道有没有碰坏老逻辑。

那些凑出来的测试覆盖率,全是自欺欺人

之前去一个做本地生活的团队做交流,他们贴在工位墙上的覆盖率看板写着92%,我点进去翻了下测试用例,差点笑出声。

所有的测试用例都堆在核心逻辑的外围,真正算补贴金额的核心函数,只有两个测试用例:传满100减10的参数,断言返回的金额正确。连个边界值都没测。

这覆盖率有啥用?

后来他们改了个阶梯满减的逻辑,所有测试全过,上线之后才发现,用户凑单金额刚好等于满减阈值的时候,系统会重复计算一次折扣,光当天就多补了三万多的券。

查了半天才找到原因:之前的测试用例根本没覆盖“金额刚好等于满减门槛”这个分支。

很多人对单元测试的误解就在这——觉得覆盖率数字好看就完事了。实际上好的单元测试从来不是对着代码行数凑数的,你每写一个会影响业务结果的分支,就跟着写一个对应的用例。比如你加了“付费会员可以多叠加一张专属券”的逻辑,那你就写个测试:普通用户不能叠,付费会员可以叠,叠完的金额算得对。就这么简单。

单元测试覆盖率报告无效分支标注截图
单元测试覆盖率报告无效分支标注截图

改老代码之前,先补最痛的那部分测试

肯定有人会说,团队手里的遗留代码几千万行,全补测试要补到什么时候?

对啊,没人让你全补。

你要改哪个函数,就先给哪个函数补测试。

比如你现在要动那个四千行的满减计算函数,就先把线上真实跑了三年的核心场景,挨个写成测试用例:

用户满300减50,实付250,断言正确。

用户同时有满减券和20元无门槛券,叠加之后实付230,断言正确。

用户买了299.99的商品,没到满减门槛,只扣无门槛券,实付279.99,断言正确。

过期的券,哪怕金额够,也不能用,返回“券已过期”的错误码,断言正确。

就这几个用例,写下来也就二十分钟。

你把这些用例跑通了,再去改你要加的新逻辑。改完跑一遍测试,全过了再提交。

你怕啥?

上次帮那个凌晨打我电话的朋友补这几个用例,花了一个半小时。后来他们要加跨店满减的逻辑,改了二十多行代码,跑了一遍测试,十分钟就上线了,再也没出过错。

真的比你上线之后被财务追着对账强一万倍。

我自己算过一笔账:写核心业务代码的时候,每花10分钟补单元测试,后面改逻辑、排查线上问题的时候,至少能省1个小时的时间。这笔账怎么算都不亏。

上周跟那个朋友吃饭,他说现在团队定了个死规矩:要碰老代码,先补三个核心场景的测试,不然不许提交。

现在他们大促之前,再也不用拉着全组人通宵对账了。