测试驱动开发是一种先开发测试的先进技术03
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
测试驱动开发是一种先开发测试的先进技术,即你在编写足够的产品代码用于测试和重构之前就编写测试。测试驱动开发的初衷是什么呢?一种观点是TDD是一种规范而不是校验。就是说它是在你编码之前就惯穿设计的一种思考。另一个观点是TDD是一种编码技术,就像Ron Jeffries喜欢说的那样TDD可用来编写干净的可工作的代码。尽管我把决定权留给读者,但我想以上两点各有其道理。
目录
一、什么是TDD?
二、TDD与传统测试
三、TDD与文档
四、测试驱动数据库开发
五、TDD与敏捷模型驱动开发(AMDD)
六、为什么使用TDD?
七、小结
八、工具
一、什么是TDD?
先行测试开发(Test First Development - TFD)的步骤已在图一的UML活动图中概括出来了。首先快速加入测试以及足以使测试失败的代码。然后运行测试,当然可能由于速度的原因你只决定运行整个测试的一小部分,用于确认新测试的失败。你修改你的功能代码使此次测试通过。第四步是再测试此代码,如果还是失败那你可能还要继续修改你的代码直到通过测试为止。一旦通过试就再回到第一步重新做起(可能你首先要重构设计中任何重复的代码,从TFD转到TDD)。图一:先行测试开发步骤
我喜欢用以下简单的公式描述TDD:
TDD=TFD+重构
TDD和传统的测试是完全的两回事。你使用写功能代码之前编写测试代码这种方式来替代先写功能代码再追加测试代码。更多的,你以很小的步骤来做此事—一次只写一个测试代码和相应的功能代码。一个遵循TDD开发步骤的程序员会因为不是当前的功能代码而拒绝写新的直到有一个会失败的测试代码。实际上就算是多一行代码他们都是不愿意的,除非有新的测试。一旦有了测试代码他们才会做相应的开发来确认测试的通过(你的新功能和测试代码可能会扰乱现有的测
试)。只要你的代码通过测试了,那就要重构它以确保代码质量。这在原则上听起来简单,但当你第一次用TDD开发步骤时它会证明(你)需要更多的训练,因为它很容易出现“脱节”而引起编写功能代码之前没有编写测试代码。结对编程(Williams and Kessler 2002)的一个优点就是你的搭档会帮助你跟踪。
TDD一个潜在的假设是你有一个可用的单元测试框架。尽管商业工具是一个可选的方案,但大多数敏捷开发人员还是愿意使用xUnit系列的开源工具,比如JUnit或VBUnit。没有这些工具TDD实际上是不可能的。图2是一个UML状态图,描述了人们是如何在典型的开发中使用xUnit 工具的。这个图是Keith Ray.推荐给我的。
图2:通过xUnit框架进行测试
Kent Beck,一位致力于在极限编程(XP,Beck 2000)中普及TDD的人,他为TDD定义了两条简单的规则(Beck 2003)。一,在自动测试失败时只编写业务代码;二,消除任何你找到的任何重复(的代码或设计)。Beck解释了这两条规则是如何展现复杂的个体以及如何组织行为:
∙你进行有机的设计,运行的代码能根据结果产生回
馈;
∙你自己编写测试,不可能因为人家为你写测试而一天
等20次;
∙你的开发环境必须要求能对小的更改作出快速响应
(比如你要能快速编译和进行回归测试);
∙你的设计必须高内聚低偶合以使测试更容易(比如你
的设计高度规范化,这样能让你进步而且你的系统也
会更容易维护)。
∙对程序员来说则暗示着应该学习如何进行有效的单
元测试。Beck的经验认为好的单元测试:
∙运行快(它们有快速安装,执行和中断);
∙在隔离环境中运行(你可以重新部署它们);
∙使用数据以便容易阅读和理解;
∙在需要的时候使用真实数据(如产品数据的拷贝);
∙一步一步向你的目标前进;
二、TDD与传统测试
TDD原本是一种编程技术,它间接地确保你的代码能彻底地被单元测试检查。可是,它能做的不只这些。你仍然需要参考传统的一些测试比如功能测试、用户验收测试、系统集成测试等等。大部分的这些测试都可以在项目开始之前就进行,如果你选择这么做的话(而且你也应该这么做)。实际在XP里,项目负责人指定的用户验收测试在编码时或是更早就开始进行了。告诉项目负责人系统能适应需求,增强他的信心!
一个好的传统测试能发现一个或多个漏洞,此理论同样适用于TDD。当一个测试失败的时候你必须进行改进因为你知道你需要解决这个问题。更重要的是,当你的测试不再失败的时候你就可以清楚地知道成功了多少。TDD增强了你的信心因为它适应了预先定下的需求,你的系统能正常运行了,因此你再自信地开发下去。
像传统的测试一样,项目风险越大,就要更彻底地进行测试。传统测试和TDD中你都不可能为着完美而奋斗,代之的是测试系统的重要性。为了解释敏捷模型(Agile Modeling, AM),你应该“带着目的去测试”、知道为什么要测试以及测试是在什么等级。TDD为你带来的有趣的间接影响是,你的代码测试覆盖率是100%—每一行都被测试过了—这是传统测试所不能保证的(虽然它们推荐这么做)。一般来讲我会很负责任地说TDD的结果比传统技术更有意义。
三、TDD与文档
大部分的程序员都不喜欢读现有系统的文档,他们更喜欢看代码。这也无可厚非。当要了解一个类或是操作的时候他们一般是先去找相关的示例代码调用。书写良好的单元测试正是这么做的—它为你的功能代码提供了工作指引—作为结果的,单元测试有效地成为你技术文档有意义的一部分。这暗示着预期要用到的文档必须实事求是。
四、测试驱动数据库开发
直到写这篇文章的时候,在敏捷开发团队中仍然在讨论一个问题:“TDD是否能用于面向数据的开发?”正如你在图1中所看到的处理流程那样,没有一个步骤是用某种特定的语言来描述的,比如JAVA或是C#,即使它们是TDD的典型使用环境。为什么不能在你修改数据库结构之前写一个测试?为什么你不做一些改变,运行测试,然后根据要求重构你的(数据库)结构?在我看来你只需要选择这样的工作方式就行。
我猜测在短期内数据库TDD没有一般应用程序的TDD好使。第一个挑战就是工具的支持。虽然单元测试工具,比如DBUnit,在写此文的时候现在已经表现出其可用的一面了。一些DBA 正在提升他们测试的质量,但我没有看到哪个TDD接近了数据库开发的目标。另一个挑战是单元测试工具在数据库团队中并不能被很好的接受,虽然这正在被改变。所以我希望的是在以后的几