全面单元测试是保证软件开发过程质量的关键策略,但迄今为止并没有为人们广泛接受。本文考查了妨碍全面单元测试的"拦路石",并介绍了来自 IBM Rational 软件公司旨在克服这些拦路石的新技术。

去年春天,我的一位同事获悉他的汽车由于某些部件的缺陷,正在被召回。事实证明,这对他来说仅仅是稍微不便。他把车开到经销商那里,人家免费为他更换了有缺陷的部件,不到几个小时,他拿回了车。除了在经销商那里浪费了几个小时的工作时间之外,他并没有受到什么损失。但从汽车制造商的角度讲,问题要严重得多。这次召回涉及到的车主有 16500 人之多,给制造商造成的损失超过 1.14 亿美元。这真是一笔不小的费用!

由此不难得出结论,只要制造商能在产品生产的早期发现部件的缺陷,能节省一大笔费用。即便是在所有的汽车都已经装配好但还没有交付的时候发现缺陷,节省的费用也是很可观的。另外还可以避免由于客户的信任度受损和同行的恶意攻击而造成的尴尬局面。我们不妨设想一下,如果制造商能在设计图纸时发现这一缺陷,那他们节省的费用又该有多少呢?

以设计求质量及全面单元测试
以设计求质量的产品生产方法包括一系列的策略、过程和实践,借助于它们,在开发过程的早期能够满足质量要求。这种方法既不是新生事物,也不是无的放矢。比如,这种方法曾被用于制造波音 777 飞机。这种飞机的超过 90% 的测试都是根据计算机设计模型来完成的。在组装第一架飞机之前,实际上只造了一个样机,这样一来,节省了几百万美元。

Steve McConnell 在他 1997 年出版的《软件项目生存指南》(Software Project Survival Guide)一书中验证了以设计示质量方法:在编码以前修复缺陷比在产品交付时或交付后要少花 50-200 倍的代价。这是个发人深省的数字且在业界引起了广泛的注意。然而,并不是所有的人都遵循以设计求质量的方法,而且事实上多年以来我们一直没有这样做,这究竟是为什么呢?这些问题很复杂--以设计求质量是一个涉及范围很广的话题,根本不可能在短短一篇文章之中将它讨论清楚。但我们可以通过探讨软件开发相关的以设计求质量的一个方面,即全面的组件测试,作为一个"敲门砖"来理解该方法,以及为什么采用该方法会有如此巨大的阻力。

为全面单元测试扫清障碍
尽管所有的软件开发人员都承认全面单元测试会带来极大利益,但实际上全面单元测试远远没有得到普及,尤其是对于中间层组件测试和缺少图形用户界面的组件测试。为何会出现这种情况呢?因为这些工作既费时又乏味。在过去,克服这些障碍经常是得不偿失。一个主要的问题在于,大多数测试是为特定组件而量身订做的,重用更是机会渺茫。由于开发团队的时间压力很大,因此他们为了赶进度而不得不将精力集中在开发应用程序本身上。通常,开发人员认为,对于每个项目如果都从头构建测试工具和存根,项目结束后再"扔掉"它们。这个过程是一种浪费。所以,他们宁愿将有限的资源都用在编写组件代码上面,而不是花在测试上面。

所幸的是,现在我们终于可以摆脱这项困扰。IBM Rational 刚刚引进了一种新技术,使我们能够进行经济高效的全面组件测试。IBM Rational 公司一直致力于为软件开发人员提供各种工具,以帮助他们在更短的时间内交付高质量的软件产品,Rational QualityArchitect 正是其中的一部分。Rational QualityArchitect 通过利用能够自动生成测试代码的可视模型,简化了组件测试。开发人员可以集中精力来创建所需的测试用例,而不用花费时间来编写容易出错并且不可重用的测试代码。

没有 Rational QualityArchitect 的组件测试
为了更好的理解为何这一新产品会使全面组件测试更加容易实现,让我们先来看一看没有 Rational QualityArchitect 的组件测试将面临哪些挑战。

图 1 所示为四个未经测试的组件。假设现在组件 B 已经可以测试了,而其他的组件(A、C 和 D)还没有准备好测试,即使准备好了,它们之中可能包含一些缺陷,影响测试结果,并且使寻找组件B中的错误更加困难。由于这些原因,开发人员通常会编写他们自己的测试驱动程序和存根,而事后将它们"扔掉"。

图 1:用于测试的四个组件

现在来考虑一下开发测试驱动程序的复杂度。一个模拟组件 A 行为的的测试驱动程序必须驱动组件 B、对其进行调用、提供一组输入,以及记录组件B的响应。同时,组件 B 所依赖的组件 C 和组件 D 的所有功能必须由存根来提供,并且根据组件B的不同输入,它们必须返回正确的结果。这听起来像一种复杂的"字母汤"烹饪法,难道不是吗?

另外,即使是在完成对单个组件的测试之后,仍然要应对许多挑战。在场景测试当中,为了测试一个给定序列的调用,必须将两个或更多的组件进行同时测试。如果客户端软件还没有完成,开发人员必须花时间来创建一个模拟的客户端以驱动特定的场景。根据一些软件开发团队的报告,他们为了创建这些测试工具所花费的时间占据总开发时间的一半以上,而这些测试工具几乎不具有重用性。

具有 Rational QualityArchitect 的组件测试
Rational QualityArchitect 为我们提供了开启经济高效的全面单元测试之门的钥匙:它充分利用了软件开发人员在开发过程的早期创建工件(即可视模型),来生成测试工具。开发人员可以用IBM Rational Rose产生可视化的模型,一旦开发人员知道各个组件所必须执行的行为,他们可以在一个模型之中将该行为记录下来。由于这些模型是用来为组件自动生成代码的,所以在这个意义上,开发人员能够使用 IBM Rational Rose 来生成的可视模型显得功能特别强大。这也正是 Rational QualityArchitect 作为Rational Rose Enterprise 包中的一个组件的原因。

对于单元测试来说,开发人员必须完成以下三个目标:

测试单个的软件组件的单个方法。
依次测试多个组件中的多个方法。
为不完整或未完成的组件生成存根,以使一个组件的测试不依赖于其他组件的存在。
这三种测试中的每一种又由两部分组成:

驱动测试过程的测试工具或框架代码
测试用例数据
这是需要做的全部工作。是不是很简单?为了阐述更具体一点,让我们来看一个例子。假设我要测试一个 Enterprise JavaBean(EJB)组件。我需要做两项工作:

首先,创建所有要连接到服务器(EJB 驻留在其中)的测试代码(A),然后实例化 bean,调用 bean 的操作并验证返回的结果。
接下来,创建用来调用单个操作的测试数据(B)。
虽然创建测试数据很具挑战性,但实际上创建测试代码更加费时,且很枯燥。

记住,可视模型中已经包含了创建测试代码所需的全部信息。可视模型包含了组件及其操作的结构描述,还有操作的参数及返回的值类型。在设计阶段或在基于现有组件的逆向工程阶段,可视模型是由分析师创建还是由开发人员创建并不重要。创建测试框架代码所需的全部输入已经"各各位"。

这时该轮到 Rational QualityArchitect 接管我的工作了。通过分析可视模型中给出的组件结构,Rational QualityArchitect 可以生成测试单个组件或涉及到多个组件的一系列操作所需的全部代码。QualityArchitect 甚至可以在真正的组件被部署之前生成存根组件作为占位符来运行。

测试代码只是解决方案的一半。我还需要测试数据。这里,QualityArchitect 使开发人员的工作容易得多。由于不必再为创建测试代码的繁琐过程所困扰,我可以将注意力集中在创建我们感兴趣的和有意义的测试数据上。QualityArchitect 甚至可以在不需要特殊的测试用例时,帮助生成随机的测试数据。当需要特定的测试数据时,QualityArchitect 可以提供一个简单的像电子数据表一样的界面来输入数据。

节省成本,节省时间,没有返工
在没有 Rational QualityArchitect 时,全面单元测试过程中的早期测试是那样的费时和低效,以致于很多公司都放弃了这项工作,尽管它的价值是显而易见的。有了 Rational QualityArchitect,使我们能够进行早期测试,因为 QualityArchitect 自动生成了测试工具和存根--且随着模型在开发过程当中的不断演进,测试工具和存根的生成也不是一次性的而是增量式的。对一个从事单元测试工作的开发人员来说,Rational QualityArchitect 实际上消除了创建一次性的测试代码的耗时耗力工作,现在所要做的只是向一个电子数据表中输入数据这样简单的工作。

更重要的是,在开发过程的早几个阶段,所有这些测试都是在可视化模型以外完成的。通过利用用于测试操作的现有资产,Rational QualityArchitect 使得软件开发团队能够采用以设计求质量的方法,而又不占用过多的软件基础开发时间。

尽管在软件业中召回事件不常发生,但存在缺陷的软件系统与存在缺陷的汽车一样具有破坏性,其所造成的后果可能比后者更加严重。在软件项目中采用以设计求质量的方法,这一目标值得我们为之奋斗。早期的测试可能为一个汽车制造商节省 1.14 亿美元,它也会为软件系统开发公司带来类似的成本节省。波音公司已经证实可以利用计算机设计来安全地测试整个飞机。Rational QualityArchitect 确保您能够在复杂的软件系统中实现同样的目标。