首页 / m.itcdj.com / 正文

单元测试覆盖率提升方法踩过坑才敢说的实操路径

上周三加班到八点半,跟组里三个后端挤在楼下黄焖鸡店的角落。

张哥咬着排骨突然把筷子一放,骂了句脏话。

说上周为了凑够部门要求的85%覆盖率,花了两天写了四十多个测试用例,结果上周六上线的会员积分接口,还是因为边界值没测到,炸了三个用户的账单。

他说那堆测试,全是对着代码行跑的,能走到那行就行,根本不管逻辑对不对。

太蠢了。

先别着急写用例,搞清楚你要的覆盖率到底是什么

说实话,绝大多数团队对覆盖率的理解,从根上就错了。

大家打开JaCoCo的报告,盯着满屏的红行就头疼,满脑子都是“把这几行跑绿就行”。行覆盖率是最基础的门槛,绝对不是最终目标。

你花了三天把所有行都跑绿,但凡有个分支没覆盖到——比如用户传了个负数的积分值,或者积分刚好卡在抵扣的边界点——该出的bug一个都不会少,对吧?

IDEA中JaCoCo单元测试覆盖率统计面板截图

很多人不知道,JaCoCo里除了行覆盖率,还有分支覆盖率、圈复杂度覆盖率,甚至很多团队根本没在用的变更覆盖率。

上次见一个创业团队,要求所有历史代码覆盖率必须到90%。组里两个应届生花了三周,把没人用的2019年的老报表接口的测试补了个遍,结果核心的支付接口覆盖率才30%。

图啥呢?

老项目里那些几年没人动过的废弃代码,你补再多测试,也挡不住新写的代码出问题。

别瞎凑数,这几个方法真的能少走两年弯路

我自己带项目这几年,摸出来的最管用的第一个原则,就是变更覆盖率优先,全量覆盖率随缘。

什么意思?

就是每次代码提交合并到主分支的时候,只统计这次改动的代码的覆盖率,要求变更部分的覆盖率必须到90%以上,老的历史代码,哪怕覆盖率只有20%,也没人催你补。

这样大家根本没必要为了凑老代码的数瞎写用例,所有的测试精力,都花在最近要上线的新代码上,性价比高太多了。

第二个要改的毛病,就是别对着实现写测试,要对着业务逻辑写测试。

很多人写测试的思路是:看这个方法里有个if(a>0),就传a=1走一遍,再传a=-1走一遍,完事。

根本不想这个方法要实现的业务是什么。

比如那个积分抵扣的方法,业务规则是满100积分抵10块,不足100不抵,超过1000的部分按8折算。你要是对着实现写,可能测了个寂寞,要是对着业务规则写,把100、99、1000、1001这几个边界值都测了,再断言返回的抵扣金额对不对,才是真的有用。

哦对,断言才是单元测试的灵魂。没有断言的测试,就是个跑代码的脚本,哪怕把所有代码都跑了一遍,也证明不了任何事。

单元测试无效断言代码示例截图
单元测试无效断言代码示例截图

还有个小技巧,写业务代码的时候,顺手把能想到的异常情况都测了。不用搞什么严格的TDD,你写完一个工具类方法,花五分钟写两个测试,一个正常输入,一个异常输入,比你最后上线前花两天补一堆没用的用例强十倍。

对了,单元测试就测你自己写的纯逻辑,外部依赖——比如数据库调用、第三方接口请求,全部mock掉就行。别搞什么连真实数据库的集成测试当单元测试用,跑一次要五分钟,还动不动因为网络波动失败,没人愿意跑的。

别拿覆盖率当KPI,不然最后坑的是你自己

别拿覆盖率当KPI,不然最后坑的是你自己
别拿覆盖率当KPI,不然最后坑的是你自己

不过话说回来,见过太多公司把覆盖率和绩效直接挂钩,到85%给奖金,不到就扣钱。

结果大家的聪明才智,全用在怎么凑数上了。

有人在代码里加个永远不会触发的if(false)分支,再写个测试专门走这个分支;有人在测试类里写个循环,把项目里所有工具类的所有方法全部调用一遍,啥断言都没有,直接把行覆盖率从30%拉到90%。

真的,见过太多这样的骚操作了。

没用。

上次有个朋友跟我吐槽,他们组为了冲覆盖率,花了两周写了上千个用例,结果上个月上线的订单接口,还是因为没测到用户重复提交的场景,出了几十万的资损。

覆盖率从来不是目的。

是你写完测试之后,顺带着出来的一个参考值而已。

上周黄焖鸡吃完,张哥回去把之前写的那四十多个凑数用的用例删了一半,留了十几个覆盖核心边界的,这周上线的积分接口,稳得很。