敏捷软件开发:原则、模式与实践——第4章 测试

第4章 测试

  编写单元测试是进行验证,更是进行设计。同样,它更是在编写文档。编写单元测试终结了许多反馈循环,尤其是功能验证方面的反馈循环。

4.1 测试驱动开发

  假设我们遵循如下3条简单规则:

  (1)除非编写了一个不能通过的单元测试,否则不编写任何产品代码。

  (2)只要编写正好导致测试不通过或者编译失败的单元测试就够了,无需再多。

  (3)只要编写能够正好使失败的单元测试通过的商品代码就够了,无需再多。

  如果遵循这些规则,我们就是以非常短的迭代周期进行工作。我们仅仅编写刚好不能通过的单元测试,接着编写正好能使得该单元测试通过的产品代码。我们以1min、2min的节奏在这些步骤间交替。

  第一个也是非常明显的效果,是程序中的每一项功能都有测试来验证它的操作正确性。这个测试套件可以给以后的开发提供支援。无论如何,我们因疏忽破坏了某些已有的功能,它就会告示我们。我们可以像程序中增加功能,或者更改程序结构,而不用担心这个过程中会破坏重要的东西。测试告诉我们程序仍然具有正确的行为。这样,我们就可以更自由地对程序进行改进。

  还有一个更重要但不那么明显的效果,是首先编写测试可以迫使我们用不同的观测点。我们必须从程序调用者的有利视角去观察我们将编写的程序。这样,我们就会在关注程序功能的时候,直接关注它的接口。通过首先编写测试,我们可以设计出便于调用的软件。

  此外,通过编写测试,我们迫使自己把程序设计为可测试的。把程序设计为易于调用和可测试是非常重要的。为了成为易于调用和可测试,程序必须和它的周边环境解耦。这样,首先编写测试迫使我们解除软件中的耦合。

  首先编写测试的另一个重要效果,是测试可以作为一种非常有价值的文档。如果想知道如何调用一个函数或者创建一个对象,会有一个测试展示给你看。测试就像一套范例,它帮助其他程序员了解如何使用代码。这份文档是可编译、可运行的。它保存最新。它不会撒谎。

4.2 验收测试   

  作为验证工具来说,单元测试是必要的,但是不够充分。单元测试验证了系统中小的组成单元按照所期望的方式工作,但是它们没有验证系统作为一个整体是工作的正确性。单元测试是用来验证系统中单个机制的白盒测试(white-box test)。验收测试是用来验证系统能够满足客户需求的黑盒测试(black-box test)。

  验收测试由不了解系统内部机制的人编写。这些测试可由客户直接编写,或者由业务分析师、测试人员及质量保证专家编写。验收测试是自动执行的,通常用一种特定的规格描述语言编写,这种语言比较适合非技术人员进行阅读和使用。

  验收测试是关于一项特性的最终文档。一旦客户编写完成了验证一项特性的验收测试,程序员就可以阅读那些验收测试来真正地理解这项特性。所以,验收测试是有关系统特性的可编译、可执行的文档。简而言之,验收测试是真正的需求文档。

  此外,首先编写验收测试对于系统的架构具有深远的影响。为了使系统具有可测试性,就必须要在高的系统架构层面对系统进行解耦合。例如,为了使验收测试无需通过用户界面(UI)就能够获得对于业务规则的访问,就必须要以满足这个目的的方式来解除用户界面和业务规则之间的耦合。

  在项目迭代初期,会受到手工的方式进行验收测试的诱惑。但是,这样做使得迭代的初期就丧失了由自动化验收测试的需要带来的对系统进行解耦合的促进力,所以是不明智的。在开始第一次迭代时,如果非常清楚地知道必须要自动化验收测试,就会做出非常不同的系统架构方面的权衡。并且,正如单元测试可以促使你在小方面做出优良的设计决策一样,验收测试可以促使你在大的方面做出优良的系统架构决策。

  我们还没有编写任何代码,也没有进行任何设计。这是开始考虑验收测试的最好时机。基于意图编程再一次称为有用的工具。我们应该以我们认为验收测试应该有的样子去编写它们,然后可以据此来设计系统。

  我们想使验收测试便于编写并且易于改变,我想把它们放置在一个协作工具中,并且可以通过内部网络进行访问,这样可以随时运行。可以使用开源的FitNesse工具。在FitNesse中,可以以简单的Web页面的形式编写每个验收测试,并从Web浏览器来访问和运行。更多关于FitNesse的内容这里就不再介绍。

结论
  测试套件运行起来越简单,就会越频繁地运行它们。测试运行得越多,就会越快地发现和那些测试的任何背离。如果能够一天多次地运行所有测试,那么系统失效的时间就绝不会超过几分钟。这是一个合理的目标。我们绝不允许系统倒退。一旦它工作在一个特定的级别上,就绝不能让它倒退到一个稍低的级别。

  然而,验证仅仅是编写测试的好处之一。单元测试和验收测试都是一种文档形式。这样的文档是可以编译和执行的。因此,它是准确和可靠的。此外,编写测试所用的语言是明确的,并且非常易于其观看者阅读。程序员能够阅读单元测试,因为单元测试是使用程序员编程的语言编写的。客户能够阅读验收测试,因为验收测试是使用简单的表格式语言编写的。

  测试最重要的好处就是它对于架构和设计的影响。为了使一个模块或者应用程序具有可测试性,必须对它进行解耦合。越是具有可测试性,耦合关系就越弱。全面地考虑验收测试和单元测试对于软件的结构具有深远的正面影响。

摘自:《敏捷软件开发:原则、模式与实践(C#版)》Robert C.Martin    Micah Martin 著

时间: 2024-09-30 14:29:42

敏捷软件开发:原则、模式与实践——第4章 测试的相关文章

敏捷软件开发:原则、模式与实践(笔记)

一.敏捷软件开发宣言 1.个体和交互 > 过程与工具 a)人是获得成功最为重要的因素: b)合作.沟通以及交互能力要比单纯的编程能力更为重要: c)团队的构建要比环境的构建重要. 2.可以工作的软件 > 面面俱到的文档 a)文档应该短小并突出主题: b)在给新的团队成员传授知识方面,最好的两份文档是代码和团队: c)直到迫切需要并且意义重大时,才来编制文档. 3.客户合作 > 合同谈判 a)成功的项目需要有序.频繁的客户反馈. 4.响应变化 > 遵循计划 a)构建计划时,应该确保计

敏捷开发-原则 模式与实践(1)

敏捷开发-原则 模式与实践 这的确是一本关于开发者的好书,对于我们开发者.研究人员,它提出了一个开发的全新的价值观(对我来说),甚至人生都有启发.需要认真阅读. 书中总结了敏捷开发的实例,确确实实更够感觉到对于项目的完成大有裨益,有种相读恨晚的感觉.想想自己之前的开发状态,想想自己导师安排公司项目的情况,就是低效率,就是小儿科,就是书上批评讽刺的那样,这正是开发者十几年开发智慧的结晶,前人的经验,前人的智慧,激发了我的阅读的快感,我获取知识的兴奋感,激发了我的成就感. 阅读前两天(结合思维导图)

敏捷软件开发 – STATE模式

地铁旋转门 最直接的实现FSM策略的方式是使用嵌套switch/case语句. public enum State { LOCKED, UNLOCKED }; public enum Event { COIN, PASS }; public class TurnStile { private State state = State.LOCKED; private TurnstileController turnstileController; public TurnStile(Turnstile

敏捷软件开发原则

敏捷软件开发原则 ----<敏捷软件开发原则.模式与实践>学习笔记 最近在系统地学习并且有意地在工作中实践敏捷软件开发,文章乍看起来,都是一些说教性.理论性,比较无聊的东西. 但是如果静下心来结合自己自身的经历.思考地去阅读,可能会发现,有的观点确实很赞同,然而有的可能会有自己的想法. 以下是在<敏捷软件开发 原则.模式与实践>一些读书笔记,斜体字是直接摘录于书本,非斜体字是自己的一些理解.   一.尽早的,持续地交互有价值的软件来使客户满意.初期交付的系统功能越少,最终交付的系统

敏捷软件开发 – FACADE模式和MEDIATOR模式

FACADE模式 Db类使得Application类不需要了解System.Data命名空间中的内部细节.它把System.Data的所有通用性和复杂性隐藏在一个非常简单且特定的接口后面. 像Db这样的FACADE类对System.Data的使用施加了许多规约.它知道如何初始化和关闭数据库连接.它知道如何将ProductData的成员变量转换成数据库字段,或反之.它知道如何去构建合适的查询和命令去操纵数据库.它对用户隐藏了所有的复杂性.在Application看来,System.Data是不存在

[书摘]《敏捷软件开发: 原则、模式与实践》第一部分:敏捷开发

面向对象设计的原则 单一职责 开放-封闭 Liskov替换原则 依赖倒置原则 接口隔离原则 重用发布等价原则 共同封闭原则 共同重用原则 无环依赖原则 稳定以来原则 稳定抽象原则 人的重要性 交付产品的关键因素是人,而不是过程.(敏捷 Agile) 人与人之间的交互式复杂的,并且其效果从来都是难以预期,但却是工作中最为重要的方面. ------ Tom DeMacro 和 Timothy Lister<人件> 有凝聚力的团队将具有最强大的软件开发力量. 敏捷软件开发宣言 我们一直在实践中探寻更

读书笔记-敏捷软件开发 原则,模式与实践

看了一下夹在书中的发票,2010年在当当网购买的. 断断续续的也看过几次,一直没有看完过. 这次试着写写读书笔记.看看能不能坚持住.

敏捷软件开发 – OCP 开放-封闭原则

软件实体(类.模块.函数等)应该是可以扩展的,但是不可修改的. 如果程序中的一处改动就会产生连锁反应,导致一系列相关模块的改动,那么设计就具有僵化性的臭味.OCP建议我们应该对系统进行重构,这样以后对系统再进行这样那样的改动时,就不会导致更多的修改.如果正确地应用OCP,那么以后再进行同样的改动时,就只需要添加新的代码,而不必改动已经正常运行的代码. OCP概述 遵循开放-封闭原则设计出的模块具有两个主要的特征.它们是: 对于扩展是开放的(open for extension).这意味着模块的行

敏捷软件开发 – SRP 单一职责原则

SRP:单一职责原则  一个类应该只有一个发生变化的原因. 为何把两个职责分离到单独的类中很重要呢?因为每一个职责都有变化的一个轴线.当需求变化时,该变化会反映为类的职责的变化.如果一个类承担了多于一个的职责,那么引起它变化的原因就会有多个. 如果一个类承担的职责过多,就等于把这些职责耦合在了一起.一个职责发生变化可能会削弱或抑制这个类完成其他职责的能力.这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到的破坏. 有两个不同的应用程序使用Rectangle类.一个应用程序是有关计算几何

敏捷软件开发 – ABSTRACT SERVER模式、ADAPTER模式和BRIDGE模式

设计运行在简易台灯中的软件.台灯由一个开关和一盏灯组成.可以询问开关是开着还是关着,也可以让灯打开或者关闭. 下面设计了一个简易的模型.Switch对象可以轮询实际开关的状态,并且可以发送相应的turnOn和turnOff消息给Light. 这个设计违反了两个设计原则:依赖倒置(DIP)和开放-封闭(OCP).对DIP的违反是明显的,Switch依赖了具体类Light.DIP告诉我们要优先依赖于抽象类.对OCP的违反虽然没有那么明显,但是更加切中要害.我们之所以不喜欢这个设计是因为它迫使我们在任