追求 UT 覆盖率的时候,到底在追求些什么

最近觉得 UT 真的是一个反人性的存在,因为在写 UT 的时候,往往是刚刚修改完业务代码,对应的逻辑在脑海里还非常清晰,这时候写 UT 更像是一个单纯的体力劳动。

为了 CI 的覆盖率而不得不做的一件事。

所以想聊一聊单元测试,以及背后的覆盖率那个数字,究竟有什么意义。

UT 的意义一定不是存在于写 UT 的时候,因为那时最不容易出 bug,而 UT 的白盒测试也不会挖掘出脑海之外的场景。

一个容易想到的场景是,若干个月之后的其他人(往往是自己)不记得对应的具体逻辑,但是又需要做一些修改。这时候 UT 就可以作为旧代码的 guard,避免破坏之前的行为。

UT 的另一个更重要的场景,大概是写代码之前了。

一个普遍接受的观点是「不是所有代码都适合写 UT,能够写 UT 的代码,往往是更好的代码。」,这其实也等效于另一个问题「如何才能保持比较好的 UT 覆盖率?」。

提到这个问题,大家往往都会想到「拆分」,那么从小往大说:

  1. 因为测试的粒度是方法,所以需要控制单个方法的行数
  2. 同时需要控制单个方法的复杂度,一个方法尽量只做一件事
  3. 基于相同的原则,一个类也只做一件事,因为 UT 往往是以类作为单元,运行的时候只需要关注这个类,并不需要真的运行其他类的逻辑,否则效率会非常低。

所以可以发现,并不是在真的写 UT 的时候,才去思考如何写 UT。UT 会影响如何编写之前的业务代码,小到方法的拆分,大到组件的拆分,这其实是依赖注入,会影响项目的整体结构。

一个 UT 覆盖率高的项目,更可能是结构清晰、职责明确的项目。这时候,可以把覆盖率作为对项目技术上的信心。 UT 从来都不是目的,没有 UT 的项目,也有可能是一个好项目;但是比较好的 UT 覆盖率,可以表示这个项目面对需求频繁变化的健壮性,以及不断翻新重构优化之下的生命力。