主题:【原创】闲聊敏捷编程——测试驱动开发(一) -- 代码ABC

2009-05-27 12:46:14代码ABC
【原创】闲聊敏捷编程——测试驱动开发(一)

测试驱动开发(二)

几乎所有的敏捷编程方式都会采用测试驱动开发过程。所以我先聊聊测试以及测试驱动开发。

软件测试有一句名言——就算是非计算机专业大多也曾听过。原话是:“程序测试是表明存在故障的非常有效的方法,但对于证明没有故障,测试是很无能为力的”。这句话是图灵奖——计算机界的炸药奖——获得者Edsger Wybe Dijkstra(这个名字饶舌得很)说的。而魔鬼的翻译就是再严密的测试也不能证明程序的正确性。魔鬼接着说,但是,为了避免一些愚蠢的错误,你必须进行测试!当然,那些无法避免的错误也是愚蠢的。

无可奈何的是所有软件工程的书都会强调测试的重要性,同时会花相当的篇幅讲解各种测试方法。当你按照软件工程要求组织测试的时候,你会发现你走进了魔鬼的乐园。测试是要花时间的,花人力的,也就是说成本高昂。一般项目的测试开销和开发的开销是同一量级的。精力花出去了,而回报是不能证明程序是正确的,这无论如何都是一件令人沮丧的事情。当你准备咬牙认命的时候,你有可能听到魔鬼嘲讽的声音——你会测试吗?事实上许多程序员(包括有些年头的程序员)是不懂如何写测试的,或者不懂得如何写出方便测试的代码。

其实魔鬼是有弱点的,弱点就在那句关于测试的名言。测试驱动开发用一种智慧的手段推翻了那句话,也就是测试是用来证明程序是正确的!它的逻辑很简单:所有需求都是可以测试的,凡是不能测试的需求都是无法实现的。所以只要程序通过了这些测试那么程序就是正确的。那么如果测试不严密怎么办?很好办,这证明了测试是错的,程序没错!这实在是一个无法打败的逻辑,很伪科学,是吧。其实这是一种智慧。

我曾经写过一篇文章(编程随想)说写程序就是一种翻译过程,是将人们的想法翻译成代码的过程。由于用来描述想法的语言是有冗余的,而代码是无冗余的。那么在翻译过程就不可避免地会引入错误。为了减少这种错误,计算机的专家们开发许多工具和描述方式。软件工程学则规定了翻译的步骤以及每个阶段成果的格式。比如一个想法——或者说软件功能,首先要变成需求文档,再变成概要设计文档,再变成详细设计文档最后才变成代码。在这种过程中自然语言变成代码需要好几个步骤,每个步骤都可能引入错误。所以我们可以指责需求分析错了、概要设计错了、详细设计错了、代码错了。而测试驱动开发不同,它没有所谓的需求文档,而是直接把需求变成测试代码。因此如果程序通过了测试,出现错误的地方就只能是测试。注意这里测试就是需求。搞明白了这点就算是对测试驱动开发入门了。

有人会说,这和一般的软件工程的测试没有什么本质的区别嘛。软件工程是根据需求文档来编写测试的。而测试驱动开发只不过是将需求文档编写步骤省去而已。而且这样还增加了测试编写的难度。

在前一篇闲聊我说过从软件工程到敏捷开发是一个螺旋上升的过程——或者文绉绉地说叫扬弃,也就是说不少软件工程的概念被继承下来了,但他们不是一成不变的拷贝而是进化成另一种形式。比如需求分析现在进化为测试代码的开发。而测试则和代码开发以一种新的方式结合起来。

我们知道许多想法描述起来是复杂的,甚至很难用一个定义表述清楚。文科生别拍我,这里要求定义是清晰无歧异的,并且所使用的概念和规则是自洽的。不难吗?那么谁能定义西西河的认证用户是什么?

啊!认证用户!谁捞我出来?——某个很有残念的定义。

认证用户是一群不容易惹麻烦的用户——铁手的定义。

认证用户是由不小于15个认证用户赞成,不大于8个用户反对的用户。——循环定义。

认证用户是一种特权用户。——嗯,有点味道,但是什么是特权用户呢?

认证用户回帖不用验证。——好了,说到点子上了。

通常这些想法的描述会变成对概念外延的描述,也就是说像上面的定义可以描述为:如果一个用户回帖不用验证,那么这个用户就是认证用户。注意这个句式和最后那个描述的不同,这里是一个条件判断描述。也就是我们把一个想法——功能需求变成了一个测试。我们通过转换概念外延的描述构造出一系列的判断,据此定义一个概念或者一个功能。通常为了定义一个概念或功能需要若干条判据。因此我们可以写下若干个测试。不过在测试驱动开发中这些测试不是一股脑写出来的,而是逐个写出。每写出一个就要写出能够通过这个测试的代码。这样做的目的是降低分析的难度,并且在构造代码的过程中保持思路的清晰。比如完成回帖验证的判断后,我们会想起认证用户还需要在其页面上加一个图标。这样我们的程序就在这一个个测试中丰满起来。

打个有点火药味的比方。以前的方法就像开炮:方位0110,距离16000,榴弹发射!——这是需求描述。然后看看命中了没有——这是测试。没命中!向左修正10,距离修正100,发射!这还是固定目标——需求没变。移动目标就更费劲了。

测试驱动则向导弹,发射的时候只要大致对准就可以了,在飞行过程中不断有修正指令——逐步发掘出来的测试。这样不管固定目标还是移动目标,通杀。

好吧,我承认现实不是那么美好的。

我承认这种测试也是不完美的,它也会漏掉一些关键的内容。甚至本身就是错误的。但至少我们把可能出错的地方限制在一个仅此一个地方上了。这就是进步。至于如何解决上面的问题,我们下回分解。

测试驱动开发(二)

关键词(Tags): #敏捷编程#测试驱动开发资深推荐:铁手,
主题:2211199
帖内引用