UML软件工程组织

软件测试的革命
文章来源及版权归属:TheRationalEdge 作者:Sam Guckenheimer

翻译:Blueski
日期:2003-1-20

爱因斯坦在1915年发表了广义相对论,当时这还只是一项伟大的科学猜想。4年后,Arthur Eddington和一个英国科学家组成的小组完成了一项重要的实验,在实验中他们拍摄了在日蚀过程中Hyades星云的图片,该实验表明,因受日蚀影响,图片中产生了很大的误差幅度,由此证明了爱因斯坦关于空间的弯曲和光的重力效应的预测。大众媒体随即给予爱因斯坦和Eddington很高的荣誉。同时也因为他们两人都是和平主义者,所以被一起推崇为在这个饱受战争沧桑的世界上的英雄。
虽然媒体显得急不可待,但值得注意的是,广义相对论在当时的科学界仍受到广泛的争议。直到半个世纪以后,人们才终于迎来了具有决定性的实验结果。当时Thomas Kuhn写下了《科学革命的结构(The Structure of Scientific Revolutions)》一书,相对论被作为是革命性变革的完美例子-- 一种新的观念完全替代了一整套旧的信仰。
今年7月,我代表Rational Edge采访了Cem Kaner。当时他借用了Kuhn的结构对目前软件测试领域盛行的各种争议和尚未确证的理论进行了分类。

后来,Rational Edge发表了我和软件测试方面的其他专家的一些访谈。有些读者却质疑我的选择,他们会问:“这和我现在做的主要工作有什么关系?”
因此,在本文中,我想把所有这些课题放在一起,并对自己关于未来测试领域的发展的前瞻进行阐述。我可以断言的是,测试人员、开发人员、项目管理人员、公司管理人员和最终用户们都期待着看到在这10年里软件测试实践方面将要发生的大变革。其原因很简单,--软件质量的低下已经使美国经济蒙受巨大损失,NIST估计[注1],每年损失约600亿美元,而Standish组织的数据则是2000亿美元。所以改进软件质量已成为取得高投资回报率(ROI)的直接途径,只有那些把握了软件质量的企业才会赢得胜利,其余的则将被人们所遗忘。
这些实践和工具又是什么呢?我认为随着时间的发展,以下五种趋势会得到发展和应用。
1. 测试驱动型的软件开发。在软件生命周期的各个阶段中,这些阶段包括测试、需求分析、使用形像化符号进行的规格说明,以及基于UML和其它新标准的实践;
2. 探索性学习和发现,这将成为迭代开发过程的一个组成部份;
3. 组件测试和易测试性设计,这将成为软件开发不可分割的组成部份;
4. 更加重视适当的技能的应用,减少预先写好的文档,这将成为优秀软件过程的基本原则之一;
5. 使用自动化测试来取代目前严重影响测试效率的冗余繁复的人工过程。
下面让我来对这些趋势进行说明。

测试驱动型开发

这一实践在RUP过程中又称为“测试第一的设计(test-first design)”,而在很多XP( eXtreme Programming)文章中则称之为“测试第一的开发(test-first programming)”。这一设想的提出至今已有近十年了,但是直到最近才得以在开发这一层次上取得很大的支持,这要在很大程度上感谢敏捷方法组织。他们的核心思想是,在你写一行代码之前,你要先写一行对其失效所进行的测试。在该测试的描述中应包含一个程序代码实际运行的实例。Martin Fowler将这样的测试称为“带实例的规格说明(specification by example)”。
Brain Marick和其他一些敏捷测试的支持者已经提出建议,要把测试驱动开发的概念扩展到所有的层次,包括系统测试和产品级测试[注2]。Marick很清晰地表述了他的观点:“我并不想写出一套用于捕捉用户愿望的需求,取而代之的是,我要写出一套测试,一旦这些测试能够通过,产品就能使她满意。所以我放弃需求编写的步骤,而直接把需求分析加入到测试的创建过程中去。”[注3]这些测试脚本就象是可执行的规格说明,当程序代码通过了测试,那么这些程序代码也将和规格说明保持一致。
如果你的代码是使用Java,而且你的测试也在Java中测试,那么测试很可能会基于JUnit,你可能要么是一个人,要么是编写的两人组中的一个,不管是哪一种情况,都很容易看到,这时Marick的方法是可行的。Marick相信这是可伸缩的,可以适合于小团体,或者在有一个用户在场的条件下进行“交谈式测试的创建(conversational test creation)”的实践。但是,如果有人要了解需求,这些需求却是在测试设计中被捕捉的,而你并不在场,无法直接为他们进行解释,这样就存在明显的问题。在这种前提下,我并不认为测试一定要在程序语言中体现。即使对需求有了“精确”的表达,也不足以解决可理解性的问题。对于这一问题,Leffingwell和Widrig有很好的描述[注4],下图即是基于他们的观点。

图1:可理解性问题(基于Leffingwell和Widrig的观点)


实际上,Leffingwell和Widrig并没有真正地考虑到测试的问题。他们的主要目的是想让需求具备很高的可理解性,以便于让用户和投资者能够充份理解。他们没有解决这样的问题,即如何把规格说明提交给其他投资者和生命周期的其它阶段。作为一种争议,他们认为有必要保留一部份不明确性。而实际上,不明确的需求显然会让测试人员发疯。
Marick提出的测试驱动型开发方法则走向另一个极端:测试代表了需求,测试的表现形式是一些可编译、可执行的代码。但是,如果测试(需求)的唯一表现形式是代码的话,你就很难和商务人员/投资者/客户进行有效沟通,甚至也很难和其他测试人员及开发人员进行沟通。所有这些人都认为测试的形式本该是数据和流程,所以如果要以那种方式来做的话,你的测试必须变得非常容易进行交流。
一些公司正致力于为这种隔阂提供解决之道。Rational正积极参与一个OMG团体关于UML测试预定义项目,该项目可以把测试表示为数据和可视化的流程,例如顺序图和活动图。我们正在开发一些工具来对待以下三种表示方法,代码,数据和流程,并取代类似的测试视图。
在过去,我们制作了这些“实例化规格说明”,按照RUP的命名方法,可称之为“用例实现”。“实例化规格说明”和“用例实现”的相似性可以通过援引最近的一个使用Agile方法的工作团体的报告来说明:
[一个参与者]提到,和他一起工作的人都很喜欢使用“测试第一”的开发方式。当测试框架被开发好以后,特别是当用于组织运行的测试脚本被定义好时,他们的工作变得更为容易。而用例在组织起来开发脚本时会有所帮助。他们的测试可以捕捉到用户的需要。人们之所以喜欢这样的方法,其原因是它提供了他们工作所需的结构。在Agile方法中,需求以“案例(story)”的形式存在。于是,测试脚本将以接口已明确定义好的形式把这些“案例”编织在一起。这意味着结对编程的人可以根据他们需要的顺序相互测试接口[注5]。
类似地,OMG测试预定义工作组发现,将UML排列起来也很容易。你可以将一个用例实现转成为测试,这只需增加两件东西:验证工作(例如,“现在用测试来检查这个软件中的条件。”)以及裁决(例如,通过、失败,或者暂不决定)。这样的信息在规格说明中随处可以捕捉到,所以UML符号可以让我们测试人员有了表达的手段。
于是,只需要额外做一点点努力,就可以使测试对客户来说变得很容易理解:数据表和可视化流程。然后,当有软件需要测试的时候,测试已准备就绪。这一实践使测试驱动型开发在系统级测试上也具有实用的价值,因为在设计工件和测试设计工件之间确实没有什么区别。仅仅只要增加验证的注解以及进行裁决就可以了。在比较容易理解的可视化流程和数据的帮助下,你不再需要把系统设计从测试设计中分离出来。而且由于这些流程具有一个可执行的形式(生成的代码),因此可以把每一次创建作为测试来执行。这使整个团队得到了解放,并成为Kent Beck和Erich Gamma所说的"受影响的测试(test infected)":
“...这是一种测试的类型,只要使用非常小的投入即可使你成为一个更快的、更多产的、更具预见性的、压力更小的开发者。”
-Kent Beck和Erich Gamma《Test Infected: Programmers Love Writing Tests》[注6]

探索性学习

这第二种趋势认识到了这样的现实,即我们通常很难把问题描述得十分正确:需求在演变,我们需要简化(扁平化)或者把著名的“错误-代价”曲线颠倒过来。这里的核心思想是:我所说的每一项,不管是在产品、测试或者跟踪排错的第一步,都在运行软件时通过探索性发现,才使得可视化-可执行-设计-测试的整个过程和结果变得更为真实。
实时分析工具,例如Rational PurifyPlus就带有制作精良的非常明确的实例,例如发现内存错误和性能上的瓶颈。你还可以用它们来支持更广泛的探索,尤其是在由各种不同组件所构成的系统中。80%的企业级行为都是对某些组件的重新组装,包括对遗留下来的成份进行更新或补充等等,而不是从头开始设计。
在软件运行时你会发现很多新的信息,至少和设计时一样多。这也是RUP强调要把可运行的软件作为每一次迭代的组成部份的缘故。你发现的每一样东西都应该是可视的,并且放在你用于设计的同样的工件中。对运行着的系统的跟踪是一种UML的迭代,经过验证和裁决,可以转化为一种可复用的测试。数据也值得进行概括,并形成新的等价类的基础,等等。
当前,大多数探索性测试的提倡者,尤其是Kaner和Bach,都把这作为使用后即可放弃的行为[注7]。但我认为,我们一旦把所探索到的内容无缝地加入到设计中去后,就会发现,这是高度可重用的。事实上,这是实时分析的一个新的应用:通过探索而发现的有价值的东西的捕获和利用。

组件测试和易测试性设计

第三种趋势是关于理解测试人员和开发人员的相对角色,并为每种角色分配合适的工具。Rational把提供组件测试和易测试性设计作为一种最佳实践,这已有很长的历史。我认为对这些做法的采纳源自于对质量的基本理解。当前,太多的测试人员的行为都受限于规格说明的模式,其中有很多浪费。其实开发人员才有责任去保证所开发出来的软件和规格要求相一致,他们应该使用合适的工具和过程来达到这样的目的。
Boris Beizer描述了2种不同角色的区别:
独立测试的目的是提供一种不同的观察点,由此产生了不同的测试,并且在比开发人员所采用的开发环境更丰富的环境中执行测试。自我测试(self-testing)的目的是消除那些bug,这可以在相对更简单、更明确的单元/组件环境,或者低层次的系统测试中进行,并且只需花费较低的代价。[注8]
测试驱动型开发提供更大的能力。如果规格说明就是可执行的测试,而测试进行没有产生别的问题,那么就可以认为软件和规格说明相符。其它的单元测试过程也只是为了保证同样的事情:就规格说明而言,不管它们是什么内容,代码总是与之相符合的。如果不符合,那么就是开发人员的问题。Kent Beck非常清楚测试驱动型开发所带来的效果:
如果测试驱动编码的缺陷密度达到足够低的话,那么专业测试的角色将不可避免地发生改变。以前的是“成人监护”方式,而现在更类似于一种扩音器,它宣称测试要更多地验证的是系统必须做什么。[注9]
这需要整个团队接受这样的一个前提,即保证软件符合规格说明是开发人员的职责。这使得测试者可以有更多精力用于发现和避免一些别的问题,从客户或者用户的角度来看,这些问题的存在可能会使软件所应有的价值有所降低。Brian Marick针对这些冗长烦琐做法的错误性写过一篇很著名的文章[注10]。那些文档通常已说明了一个系统中的多于一半的错误,他声称,因此你就需要专门有一个过程来让你的测试人员用来发现这些问题。
易测试性的设计在这里具有重要的意义。现在很多软件已改用基于服务的构架,这种构架是基于组件的构架的一种扩展,随之而来的是新增加的复杂性,即组件可以在没有警告的情况下发生改变,其可靠性的问题是极为严重的。大多数IT经理会接受99%的可靠性,我敢打赌,他们会认为这一标准甚至已经高于他们所用的买来或构建的组件。但是,如果你构建的系统有超过100个组件,每个组件具有99%的可靠性,那么整个系统的可靠性是0.99的100次方,实际上仅有37%。顺便提一下,这也是为什么那些有高可靠性要求的市场,例如电信业,会要求“5个9”的可靠性,即99.999%。在这样的情况下,你就可以使用100个组件,综合起来仍有99.9%的可靠性。
这一基本原理实际上要求软件进行易测试性的设计,就象30年前市场形成时硬件所做的一样。 Bertrand Meyer是这一领域研究的领先者,他提出了按合约设计,其意思是把对类及调用该类的客户端之间的关系的检视作为一种正式的协议,这表达了每个团体的权利和义务[注11]。Meyer的概念已被广泛接受,其标志之一就是合约设计的规格语言WSDL的诞生,该语言是Web Service标准的核心[注12]。
我认为人们正越来越多地接受易测试性设计。易测试性已成为诸如Web Service等框架和标准的一个补充的组成部份。接口也正成为技术平台和操作系统的一部份。一个比较简单的例子是,J2EE和.NET 中预定义开放式接口和API映射,可以允许工具来检查在运行时刻环境中发生了什么。另一个真实而有益的趋势是人们正使用象Rational XDETM这样的工具,通过设计模式的方法来构建应用--而易测试性已被置入在设计模式中。模式所构造的组件包括外在的用于测试的接口 -- 即组件的一些适当的getter和setter方法。
易测试性设计的一个实用的法则是,你已在GUI和表示层的后面对业务逻辑或软件的行为进行了访问。Bret Pettichord主张,易测试性设计应该是关于可见性和控制的设计[注13]。你通过较低层获得其外在接口,从而得到所需要的可见性,在测试的时候,通过很多开放的接口来允许你直接看到软件中的声明。同样地,你需要接口来让你能够控制应用,由此你才可以避免使用GUI,而是通过自动化框架来驱动应用。

重视技能

第四种趋势是增进软件测试专业技术知识的水准。在.com流行的年代中有这样的误解,即使没有很深的测试技术知识、业务应用方面的领域知识以及充份的培训,你也能有效地进行测试。但当你面对一个分布式的应用--例如,一个特别的基于Web的应用,就会发生问题。Hung Nguyen关于基于Web应用的测试论著是这种观点最好的代表[注14]。Nguyen认为,测试人员应该知道技术是如何对他们所看到的各种错误产生影响的。他们需要对技术问题有所理解,例如配置的问题,以及他们所检查的技术本身内含的问题。各种细节上的理解,例如了解应用服务器中Bean和Container管理的持续性之间的区别,可以直接影响到你发现特定缺陷的能力。
所以,现在的测试人员除了测试本身的技术以外,还需要理解开发技术和领域知识。例如,假设你在浏览器中看到一个错误:“404 - Page not found”这样的错误可能是错误的链接所引起,也可能是因为某些服务失效而产生。一个好的测试人员并不会在出错页上就停下来,他会进一步诊断出错的原因。他不仅需要对该失效的服务具备足够多的认识和理解,而且他要通过查看其它使用该服务的页面来验证自己的猜测。这就是一种Bug隔离的重要技能。
另一种技能是成为一个很好的探索者。以前,测试方面的很多论述对计划和脚本有很多要求,但现实情况下,一个好的测试人员就是一个好的探索者。他们喜欢在测试过程中发现一些暗示,并知道怎么来进行跟踪。这样的暗示有时很简单,例如一个页面要很长时间才能加载。那么对于一个好的测试人员来说,他可能会想,这其中发生了什么?然后继续了解要通过什么路径可以进一步发现答案。James Bach所写的一些内容可能是在探索性测试方面最好的材料[注15],其中有该课题的最佳练习。我认为这显然也是一个重要的技能,每个团队都需要这样的技能。
我们在Rational学院的课程中已经非常重视如何去应用基本的软件测试技术。可以和Florida Tech的Cem Kaner一起开始那些专为测试人员提供的软件测试基本原理的新课程[注16]。该课程并不专注于测试工具,而是专注于如何成为一名很好的软件测试人员,尤其是当你正在应用迭代开发过程的时候。最后,测试人员的生产能力和开发人员的生产能力是同样重要的,只有富有经验的测试人员才能使产品让客户获得很高的投资回报率(ROI)。Rational已经发现,一个更快、更经济、更高质量的开发过程的关键就是迭代式开发过程。迭代式过程可以使测试在整个开发周期中得以提前,从而可以更早地发现错误,修改错误也相对更加容易,其成本也相对更低。
但是,我认为现在的测试人员还没有得到很好的训练以胜任迭代开发过程中的测试工作要求,项目经理也没有得到很好的训练以正确地认识测试在迭代项目中所扮演的角色,开发人员也没有得到很好的训练以得到他们需要了解的测试相关技术,例如基础等价类划分。因此我们在RUP(Rational Unified Process)和Rational学院中增加了大量关于测试的材料。而且我们还将继续扩充这些材料以帮助测试人员、开发人员和项目经理们在迭代过程的协同工作中做得更好。

自动化测试

第五种趋势是关于测试自动化方面。目前,为了实行测试自动化,测试人员和开发人员要花费80%的精力来使(自动化)测试成为可能,而只有20%被用于使(自动化)测试变得更有意义。这一可怕的事实使很多人最终放弃了测试自动化。同样,目前的自动化软件质量(ASQ) 工具提供商正花费80%的精力用于重复工作,他们必须重新创建一个基础平台来支持相应的测试和排错工作,而仅有20%的精力来为测试和开发人员提供可见的有价值的功能。
最近,Rational、IBM和其它一些公司开展了一个开发源码的项目,其目标就是要把这两个百分数颠倒过来。该项目被命名为Hyades,取自Eddington用来校验爱因斯坦理论的星云的名字,并由Eclipse.org负责。它的目标也包括加强实验性观察,测试过程及软件度量,最终实现更具实用性的测试自动化。

对于使用Eclipse的开发人员和测试人员来说,Hyades既是一种集成测试及跟踪,也是环境监控程序。Eclipse为整个测试过程提供了标准、工具和互操作性,以使测试能更早地移植到应用生命周期中去。对ASQ提供商和集成商来说,Hyades为自动化测试、跟踪、预定义、监控和资源管理提供了一个可扩展的架构和平台。和目前的测试与跟踪工具所不同的是,Hyades将提供一个统一数据模型(实现了UML测试预定义),这是一种标准的用户工作的流程,包括一套统一的API及相关工具,可以在排列的目标项之间连续地工作。

总结:测试实践的大变革

Rational和一些竞争对手尽管自己也提供商业测试工具,为什么还要加入到象Hyades这样的开放源码项目中去呢? 我的很多同事也问过这样的问题。其核心理由就是上面所说的80/20比例。所有人都很想改变这个比例。
80%的基础平台对用户来说是不可见的,它难以分辨,也难以维护。每当测试所用软件的环境条件更新的时候,(新的编译器,新的库文件,新的操作系统补丁,等等),测试工具就必须随之更新。如果你是一位富有经验的实时分析或自动化工具的用户,你可能早已感受到这种脆弱。你也许已经不止一次在考虑要更换开发环境,因为有些工具不支持一些新的版本。这一维护成本给工具提供商带来了巨大的压力,因此工具商们决定无偿地为新的引擎工作,并分享其成果,进而满足用户的需要。Hyades项目必将为我们的用户提供其价值。

对Hyades来说,它是由一系列分散的努力所组成。在我所归纳的五种趋势中,Hyades是其中的一个组成部份,它将同时为测试人员和开发人员提供新的测试支持方式。这是一种技术,它可以在生命周期的一开始就推动测试,带来工具方面更好的协同性,通过改进测试,新的效果会明显地加入到软件中去。它将为这10年里我能所能看到的在测试实践上的改革提供有力的支持。我相信这种技术,以及其它有类似目标和基础的技术,代表着我们产业的未来。我们这些已被卷入到Hyades项目中的人都有一种使命感,我们不能辜负Hyades这一名称:
让我们描画出金牛座的头部--Hyades星云中的恒星,这对我们来说意义重大,这将带给我们快乐,并使我们能够测量整个宇宙!
--Anthony G. A. Brown, Universidad Nacional Autynoma de Myxico.


备注:
1 http://www.nist.gov/director/prog-ofc/report02-3.pdf
2 Kent Beck作了一个限定,把Marick的想法改称为"Application-Test-Driven Development",可参考Kent Beck《Test-Driven Development》, Addison- Wesley, 2002, 第199页。
3 http://www.therationaledge.com/content/oct_02/f_testFirstDesign_sg.jsp
4 Dean Leffingwell和Don Widrig《Managing Software Requirements》 Addison-Wesley, 2000,第273页。
5 http://fc-md.umd.edu/projects/Agile/3rd-eWorkshop/topic4.html
6 http://junit.sourceforge.net/doc/testinfected/testing.htm
7 他们的教程材料已在以下网址中公开:http://www.testingeducation.org/
另外,Bach的网站www.satisfice.com也是很有价值的资源。
8 Boris Beizer 《Black-Box Testing》, Wiley, 1995, 第13页。
9 Beck《Test-Driven Development》,Addison Wesley, 2002, 第86页。
10 http://www.testing.com/writings/omissions.pdf
11 Meyer《 Object-Oriented Software Construction》第2版,Prentice Hall, 1997, 第331页
12 参见http://www.w3.org/2002/ws/ 和http://www.ws-i.org/
13 参见诸如http://www.therationaledge.com/content/nov_02/f_pettichordInterview_sg.jsp中的讨论
14 Hung Q. Nguyen《Testing Applications on the Web》, Wiley, 2001。
15 http://www.satisfice.com/articles/what_is_et.htm 可以作为一个很好的起点。
16 http://www.therationaledge.com/content/jul_02/f_interviewWithKaner_sg.jsp中可以找到很有价值的相关讨论。

对照:
以下是翻译时所做的部份词汇处理,以资对照:

Test-driven development 测试驱动型开发
exploratory testing 探索性测试
Design for testability 易测试性的设计
Design by Contract 合约设计
artifact 工件
interaction 迭代
stackholder (项目)投资者

 

版权所有:UML软件工程组织