开发者社区> 咖啡机(K.F.J)> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

从零开始搞基建(4)——单元测试

简介:   单元测试有助于避免尴尬、耗时的错误,将测试作为安全网只是一部分,更大部分是将测试表达为代码的思考过程。   接下来的内容提炼自《单元测试的艺术(第2版)》和《有效的单元测试》两本书。
+关注继续查看

一、质疑和回答


       在组内推广时,进度并不理想,遇到的阻碍大致可归纳为以下这几种情况:

  • 首先就是团队成员会质疑单元测试的价值,需要给出证明单元测试确实有效可行的方法和证据。
  • 其次是团队成员缺乏主动测试的意识,目前有大量的测试代码,不知道从哪里开始测试,并且花费额外的精力来维护单元测试的代码。
  • 还有就是编写单元测试大概会花费日常开发的 10% 以上的时间,而项目时间总是比较紧,无法留出充裕的测试时间。

       针对第一个问题,可用一个实验回答。让两个在技术和经验上近似的团队,分别负责两个规模近似的项目,其中一个进行单元测试,另一个不进行单元测试的时间差。

       下表是两个团队的进度和输出度量:


 

阶段

不进行单元测试的团队

进行单元测试的团队

实施7天14天
集成7天2天
测试和修复缺陷

测试:3天

修复:3天

测试:3天

修复:2天

测试:1天

总计:12天

测试:3天

修复:1天

测试:1天

修复:1天

测试:1天

总计:7天

整体交付时间26天23天
客户发现的缺陷数7111


       针对第二个问题,可引用一份研究报告,20世纪70和90年代进行的研究表明:通常,20%的代码包含了90%的缺陷。如果能找到这20%的代码,那么就能大大提升测试效率。

       但困难的就是如何找到包含最多问题的代码。其实任何团队都能告诉你哪个组件问题最多,那你就可以从这个组件开始测试。

       针对第三个问题,目前的办法是多写多测,让单元测试成为开发的一部分,不要苛求测试覆盖率,先就测试影响业务流程的核心代码。


二、测试替身


       测试替身的作用是隔离被测代码,加速执行测试,使执行变得确定,模拟特殊情况,暴露隐藏信息。

       其中隔离被测代码,使测试有针对性和容易理解,而利用测试替身实现的隔离,还有个副作用,那就是测试替身的速度要比本尊快很多。

       测试替身的类型:

  • Stub(测试桩):一个对象的所有方法只有一行,且各自返回一个适当的默认值。使用场景:只关心协作对象输送的响应。
  • Fake(伪造对象):可以返回硬编码值,而每个测试可能需要有差异地实例化来返回不同值,模拟不同场景。使用场景:所依赖的服务或组件无法供测试使用,打桩产生了难以维护的糟糕代码。
  • Spy(测试间谍):用于记录过去发生的情况,这样测试在事后就知道所发生的一切。使用场景:将其作为参数传递被测函数中。
  • Mock(模拟对象):是特殊的Spy,在一个特定情景下可配置行为的对象。使用场景:关心某些交互,即两个对象之间的方法调用。


三、设计指南


  • 避免测试中包含逻辑,不应该有switch、if-else等判断语句,for、while等循环语句。以免测试难以阅读和理解,难以复现,难以命名。
  • 只测试一个关注点,一个工作单元只有一个最终结果,例如一个返回值、系统状态的一个改变或对第三方对象的一个调用。
  • 专注检查行为而非实现,避免过度指定,只需检查最终行为的正确性即可,既不要使用多个模拟对象,也不要对一个被测对象的纯内部状态进行断言。
  • 避免复杂的私有方法,不要直接测试 private 方法。
  • 避免在构造函数中包含需要测试的代码逻辑。
  • 避免单例,单例模式会妨碍创建不同的变体。
  • 使用 new 时要当心,实例化的对象,应该仅限于不会替换为测试替身的对象。


四、测试坏味道


1)可读性

       程序员用测试的方式来表达和验证代码的假设和预期行为。

       阅读测试代码之后,就该理解代码应当做什么。程序员运行那些测试时,就该了解代码实际上在做什么。

  1. 问题:基本断言缺乏意义,因为断言的基本原理和意图隐藏在看上去无意义的单词和数字背后,造成难以理解。
  2. 改进:去掉魔法数字,改用断言方法,使用编程语言内置的 API 语法。
  3. 问题:过度断言很脆弱,并且掩盖了整体广度和深度之下的意图。
  4. 改进:识别无关细节并移除。
  5. 问题:人格分裂是指一个测试检查了多件事。
  6. 改进:去掉重复,将粗粒度的场景分离。
  7. 问题:过分保护是指在测试开头增加守卫语句和空值检查保护自己。
  8. 改进:去除冗余断言,检查要使真正的断言通过所需的中间条件。

2)可维护性

       代码从不慢慢退化,而是直接奔溃。

       测试也是如此,同样脆弱,程序员编写自动化的单元测试来尽可能地管理这种脆弱性。

       大家都知道维护噩梦是什么,你绝对不希望你的测试代码沦落其中。

  1. 问题:重复最明显的是某一个数字或字符串在代码中反复出现。
  2. 改进:将可变数据提炼到局部变量中。
  3. 问题:残缺的文件路径会使代码无法转移,只能在某个人的计算机中。
  4. 改进:避免绝对路径,选择相对路径,用流来替换文件。
  5. 问题:像素完美出现的场景包括期望和实际产生的图像完美匹配。
  6. 改进:用适当的抽象层次来表达测试,将背后的细节隐藏到自定义断言中,进行模糊匹配。

3)可信度

       软件开发其实就是在修改、演进和维护代码,如果不能信任测试,那么在即使看似最无辜的改动之后,仍然不能确信代码是否能够工作。

       接下来会围绕测试不可靠的问题来检阅测试坏味道。

  1. 问题:永不失败的测试不具有价值,给你虚假的安全感。
  2. 改进:养成运行测试的习惯,例如临时修改被测代码来故意触发一次失败。
  3. 问题:轻率承诺是指测试实际上没有测试任何东西,或名不符实。
  4. 改进:确保断言了一些事情,确切找出要检查的行为,也更容易命名。
  5. 问题:降低期望就是降低了确定性与精确性的标准。
  6. 改进:提高门槛,使测试的期望更具体。
  7. 问题:有条件的测试是在一个测试方法内隐藏了秘密条件,使测试逻辑名不符实。
  8. 改进:确保测试在每个条件分支时都有机会失败。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Go 单元测试
Go 单元测试
45 0
Go应用单元测试实践
Go应用单元测试搭建
566 0
单元测试实践
工欲善其事,必先利其器. 单元测试三剑客: - TestNg:单元测试框架 - AssertJ:断言工具 - Jmockit:mock工具
2971 0
单元测试技术栈梳理
> *keep the bar green to keep the code clean.* # 前言 单元测试是一个老生常谈的事情,可是能够深入开发人心,又能够喜欢写单元测试的同学少之又少。单元测试似乎功不在当下的事情,业务代码快速完成需求,才是王道,在工作量评估的时候,如果开发同学说要花上若干天时间来写单测,需要延后几天发布,那么PD可能就会:!#@%5*))~@#,单元测试
2396 0
+关注
咖啡机(K.F.J)
每天进步一点点 研磨生活的香甜
350
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载