首先从一段真实经历讲起,这是一段悲惨的经历。数年前的老东家盯上了一块大蛋糕,欲基于开源项目研发一套平台级别的解决方案。那时理想崇高,但经验很缺乏,步子迈的大,扯的蛋也很疼。任务紧急怎么办?大家一起来做需求,所以早中期无力构建一套 CI/CD,每当提交代码时,手工(简单的)验证下即可合入。对于一个千万行代码级别平台来说,里面种种依赖,数百路人马的代码合入一起,结果实在太美。首先几百个 API,如果没有自动化测试,手工验证的效率极其低下,覆盖率也非常低,质量难以保证;其次,各路人马交叉合入,不经意间互相影响了对方的逻辑,竟然多次出现如接口都对不上的低级错误,甚至连一个可行的开发测试环境都搭建困难,大量的时间耗费在定位问题和扯皮上。纵然长期高强度通宵,稳定的版本依旧迟迟难产,交付日期一拖再拖再再再拖。

虽然能理解任务紧急,更明白平台级别的软件研发本身就充满挑战。但是忽视自动化测试(CI)的行为为以后低效痛苦的研发埋下了伏笔,捡了芝麻丢了西瓜。

为什么需要测试

那么为什么需要自动化测试,更具体的说,为什么合入代码前,一定要跑通测试用例,甚至增加测试用例?原因主要有两点:

  • 确保本次 commit 不会影响其它部分的逻辑。
  • 确保本次 commit 的功能代码无 bug。

其中第一点是最重要因素。试想,在一个较大的项目里,你还记得一个月前写的代码吗?你还记得小伙伴们提交了什么代码?你知道这一处代码对上下游的影响吗?这时,自动化测试的好处就显而易见了,它能在较短的时间内确定本次 commit 对整体逻辑的影响,如果自动化测试用例全部跑过,意味着它影响其它部分业务逻辑的概率就很低。如果说我们在建一座大厦,自动化测试则保证了每一块砖,每一粒沙都是通过质检要求的。

《快速软件开发》指出:“修改一个 bug 的代价是在 bug 产生时修改它的代价的 10 倍。” 本人甚至觉得,对于一个较为复杂的系统,代价远远不止 10 倍。磨刀不误砍柴工,在研发流程中,自动化测试就是一把锋利的宝剑,它在一定程度上高效的保证了合入的代码的可靠性,它是快速迭代的基础之一,最终提升了研发效率,保证产品质量。

防火胜于救火,我们要通过多种手段尽量避免 bug。营造一个循序渐进,有成就感的开发状态;避免搞出一坨 shit,人人充当救火队员。

测试的分类

从层次的纬度,可以将测试分为三类

  • 单元测试
  • 集成测试
  • 性能测试

单元测试(Unit Test)是面向函数级别的测试用例,由开发人员编写,测试某个或者多个函数的功能。单元测试环境应该容易搭建和运行,运行时一般不依赖其它的服务:如数据库,缓存,第三方服务等,所以在产生其它依赖的地方往往需要 mock 框架,如此有利于提升开发效率。单元测试注重覆盖率,通常情况下,70-80% 左右的覆盖率往往满足绝大部分场景。

集成测试(Integration Test)是面向 API 级别的测试用例,由开发/测试人员编写,测试一个或者多个 API 的功能是否正常,多个组件之间是否互相配合,正常工作。集成测试环境的搭建相对比较复杂,它可能依赖数据库,缓存,第三方服务等,一般为大家共用。

性能测试(Performance Test)主要用于测试性能,分析性能瓶颈等,属于高级别的测试类似。受多种条件影响,不同应用的性能测试方式各有差异,在此不多展开。

通常情况下,每次 commit 都应该先跑通单元测试和集成测试后才能提交 merge request。每次发布上线时,一定要确保发布的版本能通过单元测试和集成测试,某些应用甚至要求跑通性能测试。

什么时候更需要测试

项目的核心程度

不言而喻,越是核心的项目,越是需要测试用例保证其代码质量。

代码量

整个项目的代码量越大,越需要测试用例来保障其质量。代码量越大,意味着越难以熟谙所有的逻辑,每次 commit 带来的不确定影响越大。个人认为,当功能代码在数千行内,自行决定是否需要测试;当功能代码超过 5000 行时,请补上单元测试,对于有 API 的应用,请补充集成测试。

开发人员数量

开发人员数量越多,越需要测试用例。当开发人员越多时,特别当水平层次不齐时,会极大的增加沟通成本,降低研发效率,增加引入缺陷的机率。所以研发人员越多,越需要一个自动化门槛,滤那些低质量的 commit。

编程语言

都说动态一时爽,重构火葬场。一般来说,动态类型的语言在运行期间做数据类型的检查,如 Python,Ruby;而静态类型的语言一样需要严格的定义数据类型,并在编译期间做检查数据类型,如 C 等。所以 Python 等动态类型语言比静态语言更需要测试。

项目研发周期

对于开源项目,一般开源项目都有丰富的测试用例,在做任何开发和迭代之前,建议先搭建 CI/CD,然后逐步迭代把车开起来。

对于自研项目,在项目开发初期,首先需要设计好架构,并对一些重要第三方模块选型。所以在项目初期的不确定性会比较大,如果过早的设计测试框架和用例,反而容易成为绊脚石,降低研发效率。当项目的架构较为稳定,核心的模块均已选型,基本功能可用时。就需要为该项目添加测试框架,补充测试用例,并达到一定的覆盖率,为以后的迭代开发提供扎实的基础。