测试驱动的面向对象软件开发读书笔记

| 分类 test  | 标签 test  浏览次数: -

前言

测试驱动的面向对象软件开发(Growing Object-Oriented Software, Guided by Tests)是一本实践指南,以编写面向对象软件最好的方式: 测试驱动开发(test driven development, TDD)。

第一章 - 测试驱动开发的要点

  • TDD核心循环:
    • 1.编写测试
    • 2.编写代码,让测试通过
    • 3.重构代码
    • 4.使被测试特征的实现尽可能简单
  • 测试驱动开发金规:
    • 1.先写一个会失败的测试
    • 2.再写一个新特征
  • 重构:
    • 局部思考
    • 局部动作

在不该代码行为的情况下改变已有代码的内部结构。重构的要点是改进代码,使其能够更好地表示它的实现的特征,提升可维护性。

  • 测试、实现、重构

tdd-loop

  • 测试的级别
    • 验收测试(acceptance test):
      • 整个系统能工作吗?
    • 集成测试(integration test):
      • 代码能与那些我们无法修改的代码一起工作吗?
    • 单元测试(unittest):
      • 对象是否做了正确的事,与它们打交道容易吗?
  • 外部品质与内部品质
    • 外部品质是系统满足客户和用户需求的程度(系统是否能工作、可靠性、可用性高、响应速度快等)
    • 内部品质是系统满足开发者和管理者的程度(系统是否易于理解、易于修改等)

每个人都能理解外部品质,它通常是开发合同的一部分。内部品质也是同样重要的,但它更难实现。内部品质让您能够应对不断发生的、未预期的变化,这种变化是软件工作要面对的显示。保持内部品质的目的,是让我们在修改系统行为是能够确保安全并可预测,因为它能够减少变更引起大量返工的风险。

  • 高内聚、低耦合
    • 当一个类与系统中其他一些部分紧密地耦合在一起,具有隐式依赖关系,或者拥有太多不清晰的职责时,就会发现单元测试难以编写或理解,所以先写测试会向我们提供关于设计的价值和及时的反馈。
    • 如果一个元素的修改迫使我们修改另一个元素,那么这两个元素就是耦合的。
    • 元素的内聚性是一个测量指标,说明他的职责是否构成一个有意义的单元。例如,一个既能解析日期又能解析URL的类不是内聚的,因为它们是不相关的概念。比如既洗衣服有洗碗的机器—它不可能把两件事都做得很好。在另一个阶段,如果一个类只解析URL中的分隔符,它也不太可能是内聚的,因为他不代表整个概念。要完成工作,必须找到其他的解析器来解析协议、主机名、资源等。具有高内聚的特征更容易维护。

第二章 - 测试驱动开发与对象

  • 角色、职责、协作者

我们尝试以角色、职责、协作者的方法来思考对象,这种方法有Wirfs - BrockMcKeanWirfs-Brock03中有更好的介绍。

对象实现了一个或多个角色,角色是一组相关的职责,职责具有执行任务或知道信息的义务。协作是对象或角色之间的交互。

  • Tell,Don’t Ask (吩咐,不要问)

也叫Demeter法则,让被调用的对象决定如何实现,对象只根据它们内部保存信息和触发消息传入的消息来决定。这种风格将得到更灵活的代码,因为扮演相同角色的对象容易替换。调用者看不到被调用者的内部结构,也看不到隐藏在角色接口后面的、系统其他部分的结构。

  • 优势
    • 1.隐藏不必要的信息
    • 2.迫使调用方明确对象间的交互

第三章 - 工具介绍

主要内容在于Java的测试框架,Python中比较好的是pytest+mock进行测试,pytest中的fixtruesetupteardown是比较重要的概念。通过assert断言进行单元测试。

第四章 - 启动测试驱动循环

行动主动会产生灵感,灵感很少产生行动

  • 创建反馈源

我们不能保证对应用设计的决定是否正确,也不能保证基于这些决定的设计是否正确,我们唯一可以信赖的只有通过建立反馈国政,尽快验证他们。

需求反馈

  • 尽早暴露不确定性

对于项目集成、编译、部署等,尽可能早点的开始做好准备,把项目的压力先放在前面,前紧后松。

  • 旧项目TDD
    • 1.构建和部署完成自动化
    • 2.增加用户场景测试,覆盖需要修改的那部分代码
    • 3.关注内部品质问题,重构代码并在添加功能时引入单元测试

测试观点

  • 1.测试越少越好,少到对自己的代码质量带了某种自信,单元测试是让你提升自己对代码的信心的,只要你感觉安全可以继续开发时就够了,不是越多越好。
  • 2.多针对有意义的错误进行测试,对比较复杂的条件逻辑异常小心,多测试容易让团队出错的代码
  • 3.开发过程中,单元测试应该来测试那些可能会出错的地方,或是那些边界情况。
  • 4.针对每个bug都有UnitTest,有两点好处: 1.bug被fixed 2.相同的bug不会再次出现
  • 5.怎么合适怎么搞,爱怎么测试就怎么测试,只要自己和团队有信心就可以了。没有必要就一定要写测试,一定要测试先行。
  • 6.测试的目的不是证明代码正确(也做不到),而是帮助发现错误,包括低级的错误。
  • 7.软件的质量不是测试出来的,而是设计和维护出来的。

写一个软件有两种思路,一种是简单到明显没有错误,一种是复杂到没有明显的错误。前者远比后者更难。

第五章 - 保持测试驱动循环

开始一个新特征时,先写一个失败的验收测试,表明系统还没有具备我们要写的那个特征,然后跟踪进度,直至完成该特征。

编写验收测试时,只使用应用领域的术语,不使用底层技术的术语(如数据库或Web服务器)。这有助于我们理解系统应该做什么,而不会将自己绑在任何内部实现相关的假定上,也不会让测试因为技术细节而变得复杂。

  • 编码之前编写特征测试好处:
    • 1.澄清目标,精确的表达需求,从测试开始以用户的视角来看系统
    • 2.单元测试则是独立检查对象或一组对象,有助于我们实现设计类

上一篇 SOLID原则     下一篇 go学习笔记2
目录导航