少年:究竟什么是单元测试啊?

Engineering is all about the trade-off.

工程就是折衷。

做工程就是要在做正确的事和正确地做事中间找到平衡。但是如果没有对正确的事正确做事都认识清楚的话,那样的折衷只是盲目的经验和放弃严谨。

pshu 曾经是一位TDD(Test Driven Development, 测试驱动开发)的原教旨主义,认为所有的软件都应该是以 TDD 的方式来写。原教旨主义很容易偏激,甚至变成恐怖主义。然而在社会(产品,运营,老板)的捶打下,我已经从代码恐怖主义一点一点变成现实的折衷主义者。

今天pshu从一个前 TDD 原教旨主义者的视角和要和大家深究下:究竟什么是单元测试?

没有对比就没有伤害

我们先一起来看看,单元测试、集成测试和系统测试在维基百科上的定义。(顺便学习下英语,不然都对不起公众号的名字了)

  • 单元测试

单元测试是一种通过对代码中的独立单元,或者一个或者多个模块,配合上相关的数据,  使用和操作步骤,来判断软件是否可用的测试方法。

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use

  • 集成测试

集成测试是指将软件模块组成起来测试的一个软件测试阶段。通常用来的验证一个系统或者组件是否满足功能需求。集成测试发生在单元测试之后,系统测试(系统验证)之前。

Integration testing (sometimes called integration and testing, abbreviated I&T) is the phase in software testing in which individual software modules are combined and tested as a group. Integration testing is conducted to evaluate the compliance of a system or component with specified functional requirements。It occurs after unit testing and before validation testing.

  • 系统测试

系统测试是为了验证一个完整集成的系统是否符合需求的测试。

System testing is testing conducted on a complete integrated system to evaluate the system's compliance with its specified requirements.

面对单元测试的定义,作为一位较真的程序员,我就会问了。大部分的语言函数里面能嵌套函数的定义,那它还是独立单元吗?一个类方法调用了,另外的一个类方法,那它还是一个独立单元吗?再如果测试的时候调用了另外一个相关模块的函数,但是测试对象还是一个函数那还是一个单元测试还是一个集成测试?

而上面单元测试的定义最大硬伤在于,用单元(unit)来解释单元测试(unit test)。

我们再挑战下系统测试的概念。如果开发的整个系统的设计非常的简单,只暴露了一两个函数接口,所有的功能全都收敛在一个模块内,也没有外部依赖,那针对这类接口的测试是不是即使单元测试,也是系统测试呢?

显然,这个针对测试范围来定义测试类型的方式,实际实践中可能会带来一定的模糊性。大家也许觉得这样的模糊性并么有什么大碍,但是 pshu 想说的是:一个清晰的单元测试的定义可以指导你写出更纯的测试用例,督促你探索更好的测试方式,倒逼你做出更好的代码设计。

我眼中的单元测试

那现在pshu 来说说自己我定义的单元测试:

在测试用例中只有你能轻易修改的代码参与了的测试,那就是单元测试。

换句话说,如果任何一个测试失败了,只需要修改你的代码就能让测试通过的测试就是单元测试。

那按照这样的标准来重新定义集成测试。

在测试用例中,有你不能轻易修改的代码那它就是一个集成测试。

什么是“不能轻易修改”的代码呢?比如,忙到要起飞的隔壁组开发的一个模块也在你的测试中一起运行的,让他们修改了排期一个月,自己对这部分的知识又不熟悉;再比如一个开源的第三方库,要改下,fork,修改,再发包一套下来,也不容易。

那什么事系统测试呢?在测试用例中,有你不可能修改的代码。比如你的用例需要到经过运营商的网络,网络对超过4k 的 url 会有截断,这样的原因让你的用例失败了,你是无法去运营商的设备上修改代码调整长度限制的,那这就是一个系统测试了。

那在新的单元测试的定义下,我们就能方便的排除哪些不是单元测试了。

如果你的测试用例中使用了网络,文件系统或者数据库,那它就不是单元测试。

如果你的测试和第三方的框架绑定在一起那也不是单元测试。

至于是什么类型的测试,就看你能修改代码的范围了。

为什么追求纯的单元测试

那满足这样定义的单元测试有什么好处呢?

首先它可以非常的,这样的单元测试必定会比集成测试和系统测试快。因为他测的只是单纯的代码,如果不是一些 CPU密集型的用例,轻松达到1ms 以内一个用例非常的简单。快所带了的最大好处就开发者不会觉得执行用例是负担,即使是上千的用例,几秒搞定,快速的反馈代码正确性。当作一件事件的反馈的速度是近乎是实时的时候,不管是在反馈系统中的人学习和成长的速度会成倍的提供,在反馈系统中的产物的质量也会迅速的提供。举两个例子就是:你在游戏引导中学习如何游戏,一会就可以入门了;再就是所见即所得的字处理器的出现,现在印刷品的质量比活字排版出来的质量不知道高多少。

这里我们还可以扩展出去一点,看看目前的大部分的开发方式:前端开发,写完代码之后要手动、自动的刷新下网页才知道结果对不对;后端开发写完代码,手动或者自动的部署,通过 api 的调试工具验证代码对不对。整体的反馈时间基本和10年前没有多大的改变。同样我在软件开发中的问题也没有太好的改善,项目延期,bug 不断。所以这也是当年 pshu 这么喜欢 TDD、单元测试的原因,它提高了软件开发的反馈时间。

第二个好处就是带来的解耦,把你的代码所依赖的框架、ORM 都解耦开,让代码关注核心业务逻辑。脱离了框架 ORM 等束缚,你也可以非常容易的测试你的业务逻辑。

这两个好处如果利用的好的话,可以在代码的开发过程中在保证代码正确性的同时给代码的设计带来快速的反馈。让你写出更好的代码。简单的讲,易于单元测试的代码一般来说也会是易于使用的代码,这一点 pshu 会继续写文章来论证。

单元测试也不是银弹

但是单元测试也有是有它的局限性的,因为他只是验证了代码的本身的正确性。代码写得对不对终究是要业务说了算的。那要如何才能验证到业务上的需要呢?那就需要系统测试,或者由系统测试分解出的集成测试。这些测试的成本自然要比单元测试更高一些,你需要设置好数据库,或者准备好依赖的调用,这些都是成本。当年 pshu 在电信行业工作的时候,测试实验室为了测试消耗的电费就是不小的数目。但是在投入成本背后的收益是更加贴近真实的环境验证软件的正确性。

系统测试所带来的验证结果正确性是单元测试没法比的。系统测试直接就是来真的。而纯正的单元测试中的的测试外部的依赖会被模拟简化,这样的测试在验证方面的作用自然非常的有限。

说清楚了这两点我们自然了解在工程实践中我们应该如何面对一个不那么纯真的单元测试的态度了。首先无论什么测试,先通过测试设定的范围帮我们做好正确性的保证。在这个保证下我们才有机会去提高代码本身的质量。如果出错了,还有一个不那么纯真单元测试会报错。当然这是一种自顶向下的角度去看测试效果。如果自底向上的实践单元测试,那就通过 TDD 频繁的重构,构建出扎实底层的模块。最终组合成你所需要的模块、系统。

好了,讲究什么是单元测试欢迎大家留言和 pshu 讨论,不要忘记分享点赞哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值