《设计模式之美》理论二:为了保证重构不出错,有哪些非常能落地的技术手段?

王争《设计模式之美》学习笔记

如何保证重构不出错呢?

  • 你需要熟练掌握各种设计原则、思想、模式,还需要对所重构的业务和代码有足够的了解。
  • 除了这些个人能力因素之外,最可落地执行、最有效的保证重构不出错的手段应该就是单元测试(Unit Testing)了。

什么是单元测试?

  • 文中举例,测试 Text 类中的 toNumber() 函数的正确性。
  • 写单元测试本身不需要什么高深技术,它更多的是考验程序员思维的缜密程度,看能否设计出覆盖各种正常及异常情况的测试用例:
    • 如果字符串只包含数字:“123”,toNumber() 函数输出对应的整数:123。
    • 如果字符串是空或者 null,toNumber() 函数返回:null。
    • 如果字符串包含首尾空格:“ 123”,“123 ”,“ 123 ”,toNumber() 返回对应的整数:123。
    • 如果字符串包含多个首尾空格:“ 123 ”,toNumber() 返回对应的整数:123 。
    • 如果字符串包含非数字字符:“123a4”,“123 4”,toNumber() 返回 null。
  • 将其翻译成代码:
    • Assert 类中有两个方法:assertEquals() 返回数字的断言,assertNull() 返回NULL的断言。
    • TextTest 类中有五个方法,分别是翻译上文的五个测试用例,输出根据情况调用 Assert 类中的两个方法。
    • TestCaseRunner 类中的 main() 方法分别调用 TextTest 类中的五个测试用例方法。

为什么要写单元测试?

1、单元测试能有效地帮你发现代码中的 bug
  • 通过单元测试也常常会发现代码中的很多考虑不全面的地方。
  • 写出 bug free 的代码,节省很多 fix 低级 bug 的时间。
2、写单元测试能帮你发现代码设计上的问题
  • 对于一段代码,如果很难为其编写单元测试,那往往就意味着代码设计得不够合理,比如,没有使用依赖注入、大量使用静态函数、全局变量、代码高度耦合等。
3、单元测试是对集成测试的有力补充
  • 程序运行的 bug 往往出现在一些边界条件、异常情况下,而大部分异常情况都比较难在测试环境中模拟。
  • 对于一些复杂系统来说,集成测试也无法覆盖得很全面。
4、写单元测试的过程本身就是代码重构的过程
  • 设计和实现代码的时候,我们很难把所有的问题都想清楚。而编写单元测试就相当于对代码的一次自我 Code Review。
5、阅读单元测试能帮助你快速熟悉代码
  • 在没有文档和注释的情况下,单元测试就起了替代性作用。
  • 单元测试用例实际上就是用户用例,反映了代码的功能和如何使用。
6、单元测试是 TDD 可落地执行的改进方案
  • 测试驱动开发(Test-Driven Development,简称 TDD)是一个经常被提及但很少被执行的开发模式。毕竟很多程序员连单元测试都懒得写,更何况在编写代码之前先写好测试用例了。
  • 单元测试正好是对 TDD 的一种改进方案,先写代码,紧接着写单元测试,最后根据单元测试反馈出来问题,再回过头去重构代码。

如何编写单元测试?

  • 针对上文 toNumber() 函数的测试用例,利用 Junit 单元测试框架重新实现一下。
  • 首先导入了 org.junit.Assert 和 org.junit.Test ,这样就省略了上文的 Assert 类和 TestCaseRunner 类。
  • 只写一个 TextTest 类即可,功能与上文中一致。
1、写单元测试真的是件很耗时的事情吗?
  • 尽管单元测试的代码量可能是被测代码本身的 1~2 倍,写的过程很繁琐,但并不是很耗时。
  • 毕竟我们不需要考虑太多代码设计上的问题,测试代码实现起来也比较简单。
  • 不同测试用例之间的代码差别可能并不是很大,简单 copy-paste 改改就行。
2、对单元测试的代码质量有什么要求吗?
  • 单元测试毕竟不会在产线上运行,而且每个类的测试代码也比较独立,基本不互相依赖。
  • 我们对单元测试代码的质量可以放低一些要求,命名稍微有些不规范,代码稍微有些重复,也都是没有问题的。
3、单元测试只要覆盖率高就够了吗?
  • 单元测试覆盖率是比较容易量化的指标,常常作为单元测试写得好坏的判断标准。
  • 不管覆盖率的计算方式如何高级,将覆盖率作为衡量单元测试质量的唯一标准是不合理的。
  • 更重要的是要看测试用例是否覆盖了所有可能的情况,特别是一些 corner case。
  • 过度关注单元测试的覆盖率会导致开发人员为了提高覆盖率,写很多没有必要的测试代码。
  • 从过往的经验上来讲,一个项目的单元测试覆盖率在 60~70% 即可上线。
4、写单元测试需要了解代码的实现逻辑吗?
  • 单元测试不要依赖被测试函数的具体实现逻辑,它只关心被测函数实现了什么功能。
5、如何选择单元测试框架?
  • 写单元测试本身不需要太复杂的技术,大部分单元测试框架都能满足。
  • 在公司内部,起码团队内部需要统一单元测试框架。

单元测试为何难落地执行?

  • 一般情况下,单元测试的代码量要大于被测试代码量,甚至是要多出好几倍。
  • 当开发任务紧了之后,就开始放低对单元测试的要求,一旦出现破窗效应,慢慢的,大家就都不写了,这种情况很常见。
  • 由于历史遗留问题,原来的代码都没有写单元测试,代码已经堆砌了十几万行了,不可能再一个一个去补单元测试。这种情况下,我们首先要保证新写的代码都要有单元测试,其次,每次在改动到某个类时,如果没有单元测试就顺便补上。
  • 还有人觉得,有了测试团队,写单元测试就是浪费时间。写好代码直接提交,然后丢给黑盒测试狠命去测,测出问题就反馈给开发团队再修改,测不出的问题就留在线上出了问题再修复。

你可能感兴趣的:(课程学习笔记)