前言
工作多年,我也曾时常问自己,如何才能做好一个单元测试?单元测试对于我来说,是“业务的回忆”,如上学时期写的日记,或某个深夜眺望星空的沉思,它们都有一个共同点,回忆!只是和单元测试不一样的是,过去的事情无法在改变,但你在写单元测试的时候代码有问题,你却可以及时修改。所谓“花有重开日,人无在少年”,在代码的世界里面,“Bug有复现时,码有待改期”。如果在单元测试里面,把这个代码的“回忆”做好,我想我们的单元测试就可以写好了。
单元测试是什么?
“来将可留姓名?”“常山赵子龙!”这段话是不是非常熟悉,强如子龙也要报上姓名,而我们要做好单元测试也要充分的了解,单元测试是什么?
**单元测试:**是软件开发过程中的一项重要活动,它涉及了对软件中最小可测试单元进行检查和验证。这些单元通常是代码的最小组成部分,比如一个函数、一个类的方法、一个模块或对象。单元测试的目的是在代码的详细级别上确保这些单元,按预期工作,能正确的执行其功能并返回期望的结果。
在上面的解释里面我们有两点需要注意,一、软件中最小可测试单元,二、正确执行并返回期望的结果,这两点可谓是精髓。你甚至可以直接理解为最小的代码块,能返回正确期望的结果,这是单元测试最直接的体现。
单元测试的价值?
大多数人包括我刚刚开始写单元测试的的时候,都不禁问自己这样做测试真的有用吗?直接让测试测不就好了吗?我曾试图逃避这一部分工作量,因为加上这一部分的时间会加大自己的任务开发量,于是我去服大佬们能不能不做单元测试,或者说单元测试没有什么用,没多大的价值,很显然,我是错误的。实际上,单元测试确实具有非常重要的意义和价值,比如:
**1.**早期发现问题:通过在代码编写初期就进行测试,可以在开发周期的早期发现并修复错误,减少后期修复成本。
**2.**提高代码质量:促使开发者编写更模块化、可读性和可维护性更强的代码。
3.促进重构:有良好单元测试覆盖的代码,开发者可以更加自信地进行重构,因为任何因重构引起的潜在问题都容易被测试捕捉到。
4.文档作用:单元测试用例可以作为代码如何使用的活生生的文档。
5.加快开发速度:减少了手动测试的需求,自动化测试可以在代码修改后迅速给出反馈,加速了开发迭代的速度。
是的,某一天写着写着突然发现单元测试其实是非常有意思的一个过程,就跟你再次总结当前业务一样,总会慢慢发现有其它的挖掘点,嗯,这块逻辑应该可以写的更好,这个判断逻辑是不是抽离出来比较好等等类似的。
说白了,写单元测试既能发现问题,也能回忆一遍我们的当时写的业务,在这个过程里面,回头看那些设计是当时写比较急的,现在是否有更好的优化,当时这样写会不会有什么问题等。这个过程,我个人给它赋值一个新的词为
“代码的自省”!
如何做好单元测试?
通过上面的解释,我们可以清晰的认识到,什么是单元测试,为什么要做单元测试,而现在我们就开始尝试做好它!首先,单元测试来源那里?你的回忆?不,是代码的回忆,是业务的回忆,是数据流向的回忆。在项目立项的时候,每个项目经理都会根据需求而评估出当前业务的功能点或者说用例点,而这些功能点的实现,就是你后面业务实现后所要写的单元测试
“历程”。
为什么提到数据流向这个概念,在我们的业务里面,无非就是“增、删、改、查”,增的是什么数据?删的是什么数据?这些业务操作里面都有一个基点,我称它为数据源。最初的数据源经过这一系列的业务操作,可以理解为业务的实现,数据会发生转化,在这个过程当中,验证这个数据从0到1的转变是否达到业务的预期,就是我们做单元测试的根本。
上面提到的最小可测试单元,我们还有另外一种解释,可以把它当成我们的单元测试覆盖度。覆盖度100%,是由当前业务的所有代码影响单元组成的,但并不是覆盖率越高单元测试就越好,举个简单的例子,比如我要开发一个用户登录的功能,输入账号密码,点击登录。就这样的一个简单的业务,如果要写到覆盖率100%,其中你必须验证所有的代码情况,包括你的点击按钮的函数,以及的账号、密码变量定义,这些变量声明、以及点“代码动作”我们是不需要验证。参考上面提到的,最小测试单元和预期结果,我们只关注那些方法影响到了你的数据源结果即可。
同时正常的把一个业务流程走完,我们的单元测试基本完成了60%。为什么只有60%?大多数人自测包括我本身也会有一种“固有思维”,这种思维只会让我们按照正常的运行逻辑去检测功能,但往往问题的发生都是出现在异常的情况下,如边界条件,极值判断等。所以测试里面有一个模块叫交叉测试,同时在单元测试里,我们也要做好异常值和边界值的判定。最终我们可以得到,做好单元测试必须遵循下面几个步骤:
**1.**找出业务数据源。
**2.**确立当前业务数据关键影响因子。
**3.**数据源经过这些影响改变后的结果是否达到预期。
**4.**在业务执行过程中的边界条件、异常值判断是否会影响业务功能
通过上面这几步,如果你的单元测试都包含了这些,基本杜绝80%的低级、普通bug了。我们可以尝试来写一个单元测试,在这个单元测试里面我们来逐步分析数据源的流向。
这个模块是我们业务系统里面仪表管理组件的仪表维护与检定,我们就拿其中的一个功能点,新增仪表数据,这个功能点为案例,书写我们的单元测试。
首先,找到基础数据源,你觉得是表单填入的数据吗?实际上不是,找数据源,我们就是看谁变化了,数据流的流向到那,新增进来的表单数据,会加到我们的列表数据集合里面,所以,在这个例子里面,列表的数据集合才是我们的数据源,它会被修改、增加、删掉。这个时候确定我们的影响因子,新增操作就是,新增进来的数据,加到我们的集合里面,整个业务功能点就实现了这个。那我们的单元测试就很简单了,如下:
1.输入表单数据
2.调用新增接口
3.查询列表是否新增成功
最终我们的成功是这样的
可以看到我们这个测试是没有问题的,其它业务基本大同小异,主要围绕一个关键点,你的最小测试单元和预期值。从上面找出数据源和相应的影响因子的步骤分析中,掌握当前功能的数据流向,就可以确定单元测试的方向,我们做好主体的单元测试和边界异常值的判断就可了。
总结
基本上单元测试就这些了,如果做到了上面说的,我们的单元测试都非常合格的。可为什么我说一份单元测试做好了也只能杜绝80%的低级bug和普通bug呢?因为我始终坚信好的代码是设计出来的,而不是测试出来的。除了单元测试外,我们还要多培养团队的编码规范,时常开展Code Review,定时去分享一些疑难杂症的解决思路。因为只有整个团队对质量的严格追求、对产品的完善追求、对代码的设计追求氛围上去了,才会对质量越发的注重,对代码设计越发的关注,对自己写出来的东西才会越发的珍视。加油吧,路漫漫其修远兮,我与大家在质量之路上一起同行~