五分钟让你彻底了解TDD、ATDD、BDD&RBE
在目前比较流行的敏捷开发模式(如极限编程、Scrum方法等)中,推崇“测试驱动开发(Test Driven Development,TDD)”——测试在先、编码在后的开发实践。TDD有别于以往的“先编码、后测试”的开发过程,而是在编程之前,先写测试脚本或设计测试用例。TDD在敏捷开发模式中被称之为“测试优先的编程(test-first programming)”,而在IBM Rational统一过程(Rational Unified Process,RUP)中被称为“测试优先的设计(test-first design)”。所有这些,都在强调“测试先行”,使得开发人员对所做的设计或所写的代码有足够的信心,同时也有勇气进行设计或代码的快速重构,有利于快速迭代、持续交付。重构的前提就是测试就绪(testing is ready),在这样的前提下,重构的风险就很低,否则就有比较高的风险。
TDD具体实施过程,可以看作两个层次,如图1所示:
在代码层次,在编码之前写测试脚本,可以称为单元测试驱动开发(Unit Test Driven Development,UTDD)
在业务层次,在需求分析时就确定需求(如用户故事)的验收标准,即验收测试驱动开发(Acceptance Test Driven Development,ATDD)。
图1 TDD的两个不同层次
先来讨论UTDD,如图2 所示。在打算添加某项新功能时,先不要急着写程序代码,而是将程序可能会碰到的特定条件、边界值、上下文等想清楚,为待编写(类或方法)的代码先写好测试脚本。然后,利用集成开发环境或相应的测试工具来执行这段测试用例,结果自然是通过不了(失败)。利用没有通过测试的错误信息反馈,了解到代码没有通过测试用例的原因,有针对性的逐步地添加代码。为了要使该测试用例通过,就要补充、修改代码,直到代码符合测试用例的要求,获得通过。测试用例全部执行成功,说明新添加的功能通过了单元测试,可以进入下一个环节。这样的流程也适合代码修改或重构,真正执行时,也不会严格按照这样的流程去做,但最基本要求是:先写好测试脚本(代码),再写产品代码并通过测试。按照UTDD做法,不是先写产品代码的类,再写测试类,而是先写测试类,再写产品的类。
图2 UTDD执行的过程
UTDD从根本上改变了开发人员的编程态度,开发人员不能在像过去那样随意写代码,要求写的每行代码都是有效的代码,写完所有的代码就意味着真正完成了编码任务。而在此之前,代码写完了,实际上只完成了一半工作,远没有结束,因为单元测试还没执行,可能会发现许多错误,一旦缺陷比较多,缺陷就比较难以定位与修正。UTDD在于促进开发人员思考功能特性的应用场景、异常情况或边界条件,写出更完善的代码,避免犯较多的错误。其次,也确保测试具有独立性,不受实现思维的影响,确保测试的客观、全面。这一点,对开发人员测试自己的代码是必要的。如果是倒过来,先写产品代码(即功能实现在前)再进行测试,那么测试会受实现思维影响。例如,我们自己写的文章自己检查,有时很明显的问题都发现不了,就是受实现思维的影响。一般来说(多数情况下),开发人员测试自己的代码有两个障碍:思维障碍和心理障碍。心理障碍是指开发人员对自己的代码不会穷追猛打,发现了一些缺陷,很可能会适可而止。我们知道,实际上缺陷越多的地方越有风险,越要进行足够的测试。最后,UTDD也确保所有代码的可测试性,每一行代码得到了测试,比较彻底地确保代码的(微观)质量。
许多研发人员不习惯UTDD这种模式,推行UTDD会遇到比较大的困难,那TDD的实施可以移到业务层,推行ATDD,即在设计、写代码之前,明确系统功能特性的验收标准,这比较容易推广实施。例如,在敏捷开发模式中,每个用户故事的描述过于简单,是不具有可测试性的。例如,开发一个在线旅游网,可以提供交通、酒店、门票等预定服务,有一个最基本的用户故事:
像这样的用户故事,如果不加验收标准,开发实现起来很容易,在数据库某个表中删除一条记录,在其它关联表上修改相应的标志位即可。但实际的业务不会那么简单,说取消就取消?不需要有一个时间提前量?取消一定成功吗?收不收相关的费用?是否需要线下处理的时间?是否需要通知用户?通过什么方式通知取消成功或失败?要回答这些问题,就是要给这个用户故事增加“验收标准”,如:
取消前,需要提醒用户再次确认
需提前24个小时取消
需要4个小时处理时间,才能知道取消成功与否
这类取消需要收取总金额10%的费用
不管取消成功与否,采用邮件和短信双重通知
用户事后可以查询取消的相关记录
需要保留客户和旅行网双向操作记录日志
这样,这个用户故事才具有可测试性,开发人员也会清楚如何实现这个用户故事,实现的结果和产品经理所期望的结果就不会有太大差异。
从ATDD演化出来一种具体落地的开发模式就是BDD(Behavior Driven Development,行为驱动开发)。BDD只是将验收标准更加明确化,可以看作是ATDD的实例化,即列出用户故事所可能遇到的应用场景,而且将这种应用场景的表达方式规定为GWT格式,即:
BDD再往前推进一步,就是需求实例化(Requirements By Example,RBE),更加明确需求的具体表现。还是以上面用户故事为例,可以建立类似下列内容的需求实例化。
需求越明确,用户、产品经理、开发与测试等之间的理解就越一致(on the same page),更不产生偏差和误解,有利于开发和测试的工作。基于RBE,开发人员写产品的代码,测试人员可以独立写测试的代码,产品经理的工作也会变得轻松,不需要太多的解释、不需要回答开发和测试的各种问题。
从需求角度看,BDD和需求实例化比较彻底地明确需求,统一用户、产品经理、开发与测试等认识,让大家处在一个层面上,使研发工作更高效。
从测试角度看,需求即测试,产品的需求就是测试的需求,需求可以被执行,即一步到位,将需求变为自动化测试脚本,开发出来的功能特性随时可以被自动验证。
TDD一改以往的破坏性测试的思维方式,测试在先、编码在后,更符合“缺陷预防”的思想。这样一来,编码的思维方式发生了很大的变化,编写出高质量的代码去通过这些测试,在进行每项设计、写每一行代码时都要想想用户的真实需求、应用场景和一些例外等,确保实现的功能特性符合预期,并具有健壮性。测试,也从以前的破坏性的方法转移到一种建设性的方法中来。在这种积极心态的影响下,开发人员的工作效率和产品的质量都会有显著的提高,真正实现“质量是内建的(Quality is built in)”的目标。
还可参考: