读完本书最大的体会,并不是教会我怎么写单元测试,而是让我知道了为什么要单元测试,单元测试的目的和意义是什么。当然后面也提到了一些单元测试过程中常见的问题和一些需要遵守的原则,这些原则给写单元测试提供了指导性的作用,在实践过程中还会遇到各种各样的问题,但是只要我们紧握单元测试的初衷,方向就不会偏离。
为什么需要单元测试
单元测试最终受益人:程序员本身
设计的目的
并不是为了获得一些更好的整体质量,也就说,并不是一个针对最终用户、项目经理和开发组长的工具;而是由程序员自己来完成,并且最终受益的也是成员自己。
思考?
刚开始觉得目的并不正确,但是仔细体会单元测试的真正目的并不是获得整体质量,而是为了让我们(开发、测试)的工作更加轻松。为什么这么说?就拿我们现在团队来说,没有单元测试,你也要保证项目的整体质量,无非就是测试同学的压力大一些,没有单元测试,交付给产品的功能需求一点折扣也不能打。那么有了单元测试之后,可以让我们花费更少的精力来保证交付的质量以及后面的维护成本。
什么是单元测试
单元测试是开发者编写的一小段代码,用于检测被测代码的一个很小的、很明确的功能是否正确。
执行单元测试,是为了证明某段代码的行为确实和开发者所期望的一致。
单元测试就像能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。
思考?
说单元测试就像能执行的文档
- 如果反过来觉得自己写的单元测试,没有达到这个要求,那么单元测试应该不合格。
- 在了解开源框架的源码时,一般我们可以从单元测试入口,了解每个方法的真正核心功能。
为什么需要单元测试
不能依赖(不可靠)的代码是没有任何用处的,首先说下没有单元测试的代码
- 在跟踪调试上浪费很多时间(范围比较大)
- 发现bug后,修改会导致回退情况,或修改一个bug,产生两个bug
- 提交给测试人员质量不过关,浪费测试人员时间
- 有问题的代码上线后,造成一定损失,定位问题也比较麻烦
最终导致开发天天在改bug,测试每天在测bug,产品天天在抱怨。
而单元可以避免这种"前进一步,后退两步"的开发方法。
单元测试使用场景
- 重构,检验重构是否正确
- 修改bug,避免引入新的bug
- 添加功能,保证其他模块正确,避免引入新的bug
- 全部回归测试
常见不写单元测试的理由
- 编写单元太花时间了
- 运行测试的时间太长了
- 测试并不是我们得工作(没有把握的代码,扔给测试组是不负责任的)
思考?
反思编写代码花在哪里时间最多,或许我们就没有理由不写单元测试了。
怎么写好单元测试
单元测试的最终目的是让我们工作变得更加轻松,不好的单元测试有可能会适得其反
什么是好的单元测试
好的测试应该具有以下品质,合称为A-TRIP
- 自动化(Automatic)
- 彻底的(Thorough)
- 这个对于开发人员来说,并不需要完全覆盖,覆盖核心主要流程
- 可重复的(Repeatable)
- 保证单元测试环境干净
- 所有的单元测试,在任何时刻都能通过
- 独立的(Independent)
- 独立于所有其他测试
- 独立于周围的环境
- 不需要依赖外部,才能开始测试
- 保证测试环境干净
- 专业的(Professional)
- 测试代码必须以同产品代码相同的风格来编写
- 维护封装
- DRY原则
- 降低耦合
怎么写好单元测试
其实很简单,遵守上面原则,简单可读,可重复,覆盖大部分核心功能即可。
思考?
带有目的性的去写单测,不需要TDD开发,但是不能为了写单测而写,每个单元测试有应该去验证一个功能,都有其存在的目的。
FQA
测试代码比功能代码多怎么办?
测试源码就应该比功能代码多,多了很正常
测试代码性能需不需要保证?
不需要,测试代码可读性要求会更高,是可执行的文档,哪怕啰嗦一点都没关系。
单元测试一般测试哪些内容?
- Right - 结果是否正确,也就是是否满足功能需求
- B - 是否所有的边界条件都是正确的
- I - 检查一下反向关联(如测试插入功能,检查一下查询对不对)
- C - 使用其他手段交叉检查
- E - 给自己设置圈套陷阱验证
- P - 能否满足性能要求
思考?
上面是书上的说法,没必要照搬,只要最终能达到单测目的就行,当然上面的Right - BIE我们一般都会测试。
什么时候使用mock对象?
单元测试的目标是一次只验证一个方法,但是倘若遇到某个方法依赖于其他一些难以操控的东西,诸如网络、数据库等等,一种方法是自己几乎初始化了系统的每个组件,确保有足够的运行环境让它运行起来,但是这样不稳定,而且浪费时间,耦合性较高
什么时候使用Mock对象
- 真实对象具有不可确定性的行为,产生不可预测的结果
- 真实对象很难被创建
- 真实对象的某些行为很难触发(如网络错误)
- 真实对象令程序的运行速度很慢
- 测试需要询问真实对象它是如何被调用的(需要验证验证某个回调函数是否被调用了)
- 真实对象实际并不存在(和外部联调时)
总之,发生外部依赖而且比较难以模拟的情况,都需要考虑使用Mock对象