首页 / 资讯 / 单元测试覆盖率提升方法 从项目实战里摸出来的实在经验

单元测试覆盖率提升方法 从项目实战里摸出来的实在经验

上周跟南京组里的后端兄弟熬到两点,整理季度验收的测试文档,领导甩过来的覆盖率报告明明白白写着42%。 人都麻了。 那些当年赶618大促需求写的代码,有的连入参校验都没做,有的在一个两百行的函数里塞了四层if嵌套的业务分支,甚至还有直接硬调第三方支付接口没做mock的,真要一个个补测试,光靠通宵熬根本填不上这个窟窿。 说实话,之前我也以为提升覆盖率就是对着报告里飘红的行瞎写几个测试,凑够数字交差就行。直到去年我们组为了赶季度KPI,硬写了三百多个测试用例,一周之内把覆盖率从41%冲到77%,结果上线第三天就炸了——有个余额退款的分支,我们写的用例直接把入参写死成正常值,根本没覆盖到第三方接口超时的异常场景,差点把十几个用户的退款金额算错。

先把假覆盖率的水分挤干净

很多人提覆盖率提升,第一反应是加新的测试用例。错。 我们当时第一步做的根本不是写新用例,而是把仓库里现有的所有测试用例全过了一遍。规则很简单:只要这个用例没有任何断言,不管它跑的多顺,直接删掉。 删完的结果是,之前显示的42%覆盖率直接掉到28%。组里刚毕业的小孩当时脸都白了,说这半个月写的测试全白干了。 真不是白干。那些没有断言的用例,本质上就是把函数跑了一遍,哪怕函数返回错误值、哪怕逻辑完全走偏,测试套件照样全绿。这种覆盖,全是自欺欺人的数字泡沫。
无断言无效单元测试用例统计Excel清单
挤完水分我们才发现,之前的所谓覆盖率,有差不多三分之一都是凑数的无效用例。光清理这一步,就给后面的提升省了至少一半的无用功。

别上来就啃最硬的屎山

挤完水分的真实覆盖率是28%,离要求的80%差了十万八千里。 换以前的思路,肯定是从最核心的交易模块开始补,毕竟领导觉得核心模块的覆盖率最重要。但我们吃过亏——上次硬啃支付模块,写了三天测试,光mock依赖对象就写了三百多行,最后覆盖率只涨了2%,组里的人熬得眼睛通红,性价比低到离谱。 这次我们换了个挑软柿子捏的思路。 先列了个表,把仓库里所有的模块按两个维度打分:一个是改动频率,越高越好;一个是依赖复杂度,越低越好。优先挑那种改动多、没什么外部依赖的模块下手——比如用户手机号格式化的工具类、订单状态校验的方法、参数合法性判断的工具函数。 这些模块没有乱七八糟的外部调用,逻辑边界清晰,写测试用例快得离谱。就这么慢慢补,光把所有工具类的测试补完,我们的覆盖率就从28%涨到了51%,前后只花了两天。 真的,别上来就跟最复杂的核心模块死磕。那些模块里的依赖绕得像蜘蛛网,你写个测试得 mock 七八个外部服务,花一周时间可能覆盖率才涨1%,打击士气不说,还根本见不到效果。

把覆盖率卡点嵌进日常流程

补完工具类的覆盖率到51%之后,我们停了下来。 因为知道如果没有长期约束,过不了三个月,这些补好的覆盖率数字就会跌回原形。之前我们也搞过突击补测试的运动,补完大家转头就删用例,毕竟季度验收过了,谁还愿意为了个数字花精力维护测试。 这次我们没再搞突击加班。直接把JaCoCo的覆盖率校验嵌到了GitLab的MR合并流程里。 规则也没定的太死:不要求全仓库的覆盖率每次提交都涨,只要你这次改动的代码块,行覆盖率不低于80%,就可以正常合并;如果你提的MR把整体的覆盖率拉低超过1%,直接打回。
GitLab合并请求内嵌JaCoCo覆盖率卡点配置截图
GitLab合并请求内嵌JaCoCo覆盖率卡点配置截图
刚上这个规则的时候,组里的开发天天骂,说写个业务功能还要补测试,太耽误需求进度。 结果过了两周,大家的态度全变了。有个之前最抵触写测试的后端哥们,现在写新功能的第一反应是先把边界场景的测试写好,还跟我说,之前总觉得写测试是额外负担,现在有了测试兜底,敢随便重构老代码了,再也不怕改完偷偷把线上搞崩。 现在我们组的稳定覆盖率在83%左右,没有搞过突击加班补测试的运动,也没有为了凑数字写一堆没用的用例。 说白了,覆盖率这东西从来不是给领导看的KPI。 是你每次上线点发布按钮的时候,心里那点实打实的底气。