摘要: 单元测试应该是程序员的必备技能,而真正的编程高手应该善于把握单元测试的粒度。javascript
在前一篇博客,我说起到了最近在对后端Node.js服务进行代码重构,将Promise替换成Async/Await。这是一件痛并快乐着的事。html
当任务完成50%以后,我发现,与其说是重构,更准确的说法或许是重写。一方面,换用Async/Await自己就意味着须要修改每一个异步函数,然后端绝大多数函数都是异步的;另外一方面,做为一个有着强迫症的完美主义者,我写了大量单元测试对代码进行了一系列优化,同时修复了一些BUG,而且实现了一个新功能。前端
这里的关键词是单元测试,那么问题来了,重构代码就得了,写什么单元测试啊?这不是没事找事么,要知道单元测试彷佛比功能代码更难写。java
这是一个颇有意思的话题。node
在《玩转Node.js单元测试》中,我是这样定义单元测试的:程序员
所谓单元测试,就是对某个函数或者API进行正确性验证。shell
这样的定义很是通俗易懂,但并非很准确,严格来讲应该是错误的。由于对API测试时,会涉及到多个函数,不少时候还会依赖于数据库、缓存以及第三方服务等外部资源。所以,API测试应该属于集成测试而非单元测试。数据库
根据《JavaScript有这几种测试分类》,集成测试与单元测试应该是这样区分的:编程
单元测试指的是测试小的代码块,一般指的是独立测试单个函数。若是某个测试依赖于一些外部资源,好比网络或者数据库,那它就不是单元测试。后端
集成测试就是测试应用中不一样模块如何集成,如何一块儿工做,这和它的名字一致。集成测试与单元测试类似,可是它们也有很大的不一样:单元测试是测试每一个独立的模块,而集成测试刚好相反。好比,当测试须要访问数据库的代码时,单元测试不会真的去访问数据库,而集成测试则会。
所以,对于单元测试,更加准确的理解应该是对单个函数进行独立测试。
可是,在实际操做中,测试单个函数时,很难保证所谓的独立测试。一些函数不免依赖于其余函数、数据库、函数以及第三方服务等外部资源,这个咱们很难避免,甚至有时偏偏须要验证这些外部资源。好比,验证写入数据库或者缓存的数据是否符合预期;验证数据库或者缓存中的数据对函数行为的影响是否符合预期。
在我看来,对单个函数进行非独立的测试,不妨也能够视做**“单元测试”。简单地说,本文所讨论的单元测试**,就是对单个函数进行测试。
新功能的增长,代码复杂性的提升,优化代码的须要,或新技术的出现都会致使重构代码的需求。在没有写单元测试的状况下,对代码进行大规模修改,是一件不敢想象的事情,由于写错的几率实在太大了。
我一直在鼓励你们写单元测试,然而,有时不免偷懒。当我打算重构代码的时候,发现写的单元测试实际上是不够的,这就比较尴尬了:(
那我究竟是直接改代码;仍是先写单元测试,而后再改代码呢?这是一个艰难的决定,由于前者很难保证正确性,后者貌似须要耗费大量时间。
有一种智慧叫作“摸着石头过河”:我尝试在修改函数代码以前,补写一些单元测试。这个过程并无想象中那么痛苦,也许是由于作决定自己其实比作事情更痛苦,或者是由于我比较喜欢敲代码。
因而,我就能够开始大刀阔斧地进行重构了:换用Async/Await;优化代码组织;优化程序性能;写新功能...忙得不亦乐乎。
若是没写单元测试,我敢怎么作吗?固然不敢!出错了还得我来改啊。
若是没写单元测试,我会改得那么快吗?固然不会!大概每改一个函数都会想半天,改完而后祈祷它不会出错;修改某个函数并非一蹴而就的事情,若是每次修改都去磨叽半天,大概我如今还在敲代码而不是在写博客。
正是由于有了单元测试作保证,改起来才会驾轻就熟,效率更高。这样,既能够保证正确性,又能够节省时间。想象中单元测试会浪费很多时间,事实上彷佛并不是如此。
Fundebug是全栈JavaScript错误监控平台,支持各类前端和后端框架,能够帮助您第一时间发现BUG!
也许大多数人没有我这么喜欢折腾,不会一直去重构代码,这种状况下,难道就不用写单元测试啦?
我想答案应该是否认的。由于单元测试有不少显而易见的好处:
另外,单元测试可以提供另外一个思考代码的角度,这对于编写高质量的代码是颇有好处的。
本文聊的单元测试是针对每个函数的,那么,你在写单元测试的时候,就会去考虑合理地拆分与合并函数。由于函数的功能区分不清楚的话,是不太好写单元测试的。
敲代码的时候,咱们考虑的是函数实现,无论三七二十一,写好了就大功告成了。写测试的时候,咱们跳出了函数,从输入输出的角度去思考函数的功能,这时候,你就会去想,这个函数真的须要吗?这个函数的功能是否是能够简化一下?这个函数考虑的状况彷佛不够全面吧?这些思考,能够帮助咱们写出更好的代码。
若是你是编程高手,彷佛能够少写一些单元测试。王垠大神在《测试的道理》中是这样说的:
在我心目中,代码自己的地位大大的高于测试。我不忽视测试,但我不会本末倒置,过度强调测试,我并不推崇测试驱动开发(TDD)。我知道该测试什么,不应测试什么,何时该写测试,何时不应写,何时应该推迟测试,何时彻底不须要测试。由于这个缘由,再加上高强的编程能力,我屡次完成别人认为在短期不可能完成的任务,而且制造出质量很是高的代码。
那么问题来了,你是高手吗?根据二八原理,大部分开发者并不是高手。在下自认为编程水平还不错,也选择尽可能写单元测试。
假设你是高手,那你能保证你的团队都是高手吗?根据二八原理,一个团队里面只有少数人是高手。若是你不写足够的单元测试,他们乱改你的代码,是会出事情的。
因此说,仍是得尽可能写单元测试,不管你是否是高手。
固然,你也不能没完没了地写单元测试,不然就本末倒置了。
另外,单元测试写得越多,其边际收益是在不断下降,是得不偿失的。神奇的二八原理告诉咱们,20%的测试能够覆盖80%的问题;而剩下20%的问题,你须要写80%的单元测试。换句话说,单元测试并不能消除全部问题。所以,对生产代码进行实时错误监控是很是有必要的,这也是咱们Fundebug努力在作的事情。
在《单元测试要作多细?》中,耗子哥告诉咱们:
UT的粒度是多少,这个不重要,重要的是你会不会本身思考你的软件应该怎么作,怎么测试。
这是每个程序员都应该认真思考的问题,没有所谓的标准答案。从小接受中庸之道和惟物主义辩证法熏陶的咱们,应该能够在实践当中思考合适的测试粒度。当你学会了思考,你才能成为真正的高手。
欢迎加入咱们fundebug技术交流3群:490454644