单元测试及最佳实践

前言

镜子迷宫

在工做中或者在面试中,我常常碰到的开发人员就是对单元测试不重视,这一类基本上都表现出了一种“无知的自信”,总以为本身写的代码质量很高,直到一次次虫子(Bug)把本身咬的头破血流时,才发现原来本身的代码已经到了剪不断理还乱的状态,而每一次修改一个bug,都须要走一遍“墨镜迷宫” (看上图)。还有不少人知道单元测试或者写出了单元测试,可是就是写了一个方法,上面标注了一个[Test]属性而已,甚至不少的人单元测试上面标注的是[IgnoreTest], 每次看见这些,我都深深的感到推行单元测试之路是艰难的,是遥远的,可是我依然坚信是是渴望也可及的,只要有着深深的信念,坚强的意志,无谓的勇气,一头扎进去泥巴堆里,假以时日,当大雨来临,必将带走泥巴,今后你拔剑扬眉,哦,你不用拔剑了,由于你就是剑。。。前端

为了让更多人可以拔剑扬眉,也为了咱们公司刚入职的新人作一些培训,我精心准备了单元测试的一些知识,在此为你奉上,我尽可能用简短的语句来描述,若是你不清楚我说的某一些点,那么欢迎你发邮件给我 wangdeshui@outlook.com,我能够针对集中的点发篇文章,若是你想知道我说的全部点怎么实践,那就联系我,试试加入到咱们公司来。面试

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码中的一个很明确的功能是否正确。一般而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。数据库

执行单元测试,是为了证实某段代码的行为确实和开发者所指望的一致。所以,咱们所要测试的是规模很小的、很是独立的功能片断。经过对全部单独部分的行为创建起信心。而后,才能开始测试整个系统。编程

为何要使用单元测试

  • 单元测试使工做完成的更轻松
  • 单元测试使你的设计更好
  • 大大减小花在调试上的时间
  • 能帮助你更好的理解代码

没有单元测试

  • 任何代码都是在假定其余代码是正确无误的状况下编写的。
  • 修改一处代码时没法得知会对其余代码产生怎样的影响。
  • 任何一处改动都须要进行功能级别的总体调试。

单元测试难以推进的缘由

太花时间

不少人认为单元测试很花时间,可是想一想咱们在下面几点话的时间,我常常看到为了测试一个简单的API方法,咱们不少人必须让前端跑起来,甚至本身写一个客户端才能调用网络

  • 调试上花的时间
  • 对自认为正确的代码,花了多少时间确认代码是正确的。
  • 定位Bug所耗的时间

测试不是个人工做

不少人认为测试不是本身的工做,可是想想每次测试提出一个bug所花的时间,以及你改bug所化的时间,因此下面2点是很重要dom

  • 内在质量的重要性
  • 测试应该是辅助,好的软件是开发设计出来的,不是测试出来的

系统可测试性差

  • 系统耦合度很高,咱们须要提升咱们的团队的设计能力。

单元测试最佳实践

实践一: 三到五步

  • SetUp
  • 输入
  • 调用
  • 输出
  • TearDown

实践二: 运行快速

为何?

单元测试运行很频繁,是辅助开发的,在开发过程当中运行,若是慢影响很大函数

多快较好?

  • 单个测试小于200ms
  • 单个测试套件小于10s
  • 整个测试小于10分钟

实践三:一致性

任什么时候候一样的输入须要一样的结果工具

Date date=new Date()
    Random.next()

这样的代码都须要Mock掉,否则时间每次都不一样,结果就会不同。post

实践四:原子性

** 全部的测试只有两种结果:成功和失败**
不能部分测试经过单元测试

实践五:单一职责

一个测试只验证一个行为

** 测试行为,不要测试方法 **

  • 一个方法,多个行为    ----->  多个测试
  • 一个行为,多个方法   -----   一个测试
    这里的一个行为,多个方法通常指这个方法调用private, protected, getters, setters
  • 多个Assert只有在测试同一个行为时能够接受

实践六:独立无耦合

单元测试之间无相互调用

  • 单元测试执行顺序无关
  • 不一样的顺序无影响

单元测试之间不能共享状态

好比一个测试里设置了一个属性值,而后在另一个测试里用,若是必须共享能够放到Setup里

实践七:隔离外部调用

  • 单元测试须要快速运行,且每次结果一致,因此须要隔离一切对外部的调用。
  • 不使用具体的其它真实类,就是不要new
  • 不读数据库
  • 不读网络
  • 不读外部文件
  • 适当时候能够构造一个相同的内部文件来Mock
  • 不依赖本地时间
  • 不依赖环境变量

实践八: 自描述

  • 单元测试是开发级文档
  • 单元测试是方法的描述
    单元测试自描述命名

实践九: 单元测试逻辑

  • 单元测试必须容易读和理解的
  • 变量名,方法名,类名
  • 无条件语句,无Switch
    办法:分解if到多个测试,全部的输入都是已知的,全部的结果都是必定的(Mock)
  • 无循环语句
  • 无异常捕捉
    ** 测试预知的异常,用ExpectedException方法 **

实践十: 断言

  • 断言信息最好包含Business Information
  • 断言信息包含出错的具体信息若是失败
  • 适当时候能够封装本身的Assert
    好比:

    Assert.IsProgrammer(Jack)
    Return Jack. Cancooking() && Jack.CanCoding()

实践十一:产品代码

  • 产品代码无测试逻辑
    不能有:

  If(global.IsTest){…}

  • 测试代码和产品代码要分离
  • 不要在产品代码里有任何只供测试用的代码
  • 使用依赖注入

最后,单元测试经常使用技术及工具

下面是.NET程序经常使用的单元测试须要的技术和工具,其它语言请自信比对。

  • 面向接口编程
  • 依赖注入(Castle, Unity, Ninject)
  • Moq
  • 测试工具(xUnit) 
  • .Net Nunit
  • 代码覆盖率测试工具Ncover
  • 自动运行测试辅助工具NCrunch