探测故障的最佳时机是在开发过程的早期。如果使用统一建模语言(UML),甚至在分析和设计期间就可以发现故障。然而,软件的集成和测试十分困难,嵌入式系统更困难,由于输入和输出少,系统的可操作性和可见性都很有限。反常的系统状态尤其难以测试,因为在确定系统在某一状态下的行为前,必须使系统进入该状态。
本文提出将测试仪器(instrumentation)代码注入UML模型实现中的观点,目的是提升系统的可控性、可观察性和易测性。测试仪器可应用在开发和目标环境中,并可在模型级进行交互式系统调试。在批处理模式下,测试仪器是数据采集、初始化和测试自动化的基础。本文旨在:简要介绍基于模型的软件工程以及这些模型的实现;概述基于模型的软件的集成测试方法;确定模型系统内重要的运行时间数据和执行关键点;阐述在运行时间采集和操作模型数据的几种方案;使测试仪器能自动进行测试。
软件故障是指程序中的错误指令或计算,软件故障的执行将导致软件状态出错。当错误传到输出,并作为一个异常结果呈现在系统外时,故障就会发生。程序的可控性是指一套测试系统强迫被测程序遵循一个特定执行路径的能力,也有可能沿这条路径的执行出错。程序的可观察性是指这套测试系统发现错误状态继而指出故障所在的能力。
系统的内部状态对于确定测试的正确性至关重要。系统的输出是由系统的初始状态及其输入决定的。初始状态不同的系统,即便输入相同,输出也会不同。系统的最终状态也必须作为评估测试正确性的一部分予以考虑,因为不正确的内部状态最终会传到系统的输出,并导致错误。系统的复杂性也使得预测系统的正确输出变得愈加困难。
初始状态+输入--->最终状态+输出
在“黑匣子”测试方法中,只有系统的外部输入和输出可知。需要用一个特殊的测试激励序列将错误传给输出,以便区分错误和正确的程序。所需的特殊序列越长,程序的可测性就越小。与“黑匣子”相似,嵌入式系统的可控性和可观察性也较低。评估最终系统内部状态的结果能缩短检测误差所需的特殊输入序列,从而产生更小、更易处理的测试案例。测试仪器力求同时提高软件程序的可控性和可观察性,以获得更具可测性的程序。
在应用代码中使用测试支持仪器的技术是一种“玻璃匣”测试方法。在开发系统的UML模型时,开发者必须了解系统将要完成的任务。基于测试仪器的错误隔离策略可以将UML模型的知识运用于集成测试。系统的操作和状态在分析级比在编码级更具可见性,因为后者受到实现细节的影响。
仅从外部输入设置测试的初始系统状态需要特定的外部激励序列。异常状态下的系统操作是很多嵌入式应用中验证的关键,但生成这些初始状态并不简单。本文所描述的技术可利用测试手段,大大提高可控性和可观察性。
集成测试的步骤
集成测试可分成两个重要阶段,即动态验证和目标集成。动态验证是在开发环境下运行UML模型,其目的在于确定模型的正确性。目标集成涉及到在目标环境中集成软件和硬件。动态验证和目标集成两者都是在分析级上进行的,均使用同样的工具,即测试支持仪器。
要尽可能多地进行动态验证测试,其原因有很多:硬件的可用性、硬件/软件的分离、更短的调试周期,以及工具的使用。如果在动态验证的运行测试后,可以确信模型没有问题,目标集成的调试就可以集中在系统组件之间的接口上,或特定平台问题上。
a. 用UML建立嵌入式系统模型
将UML模型有效地用于嵌入式应用的软件工程,要求开发进程能确保:模型是严格而完整的;在不影响模型的情况下优化所生成的系统实现;系统的整体结构由进程通过多个版本及要求的升级来维持。
为达到这些目标,基于模型的软件工程采用一种转换方法,重点讨论采用这种转换方法在代码中添加测试支持,但该技术也可应用于手工实现的UML模型。这种转换方法的特点将在下文介绍。
b. 分析模型
分析是针对问题本身为其建立与实现无关的模型方案的过程。有效的分析模型是严密而完整的,而且与实现方法无关。UML是由OMG定义的一种标准符号,主要用于表达分析建模。分析过程可以产生:
域(domain)模型:这是一种UML类图,它将系统分解成独立的主题区域,称为域。这些域由包和从属箭头显示桥表示,其中后者是域之间的需求流(flow of requirement)。可以对域进行分析,或者用其它方法开发,如人工编写的代码、继承代码、从其它源生成、从某个库导入等等。域服务是组成域接口的方法。由于域为某个问题区定义了完整的规范,所以可以独立对其测试,然后再与其它域结合以便进一步测试。
信息模型:对于每一个要分析的域来说,UML类图可用于定义组成该域结构的类(class)。类之间互相关联,还可继承其它类。
情境(scenario)模型:UML序列表或UML协作图捕获某个特定域的主要情境,用于表现域服务(操作)、类服务(方法)、类事件消息及该域引用的域外服务之间的相互作用。
状态模型:对于接受事件消息的每一个类来说,UML状态图可用于捕捉类周期,并定义该类与状态有关的特性。
行为模型:对于每个域服务、类服务和状态行为,都会生成一个详细而明确的行为描述。这由一种行为语言来表达,这种分析级“编程”语言提供了完整的分析级执行基元,而不会影响实现。用行为语言来表示行为细节,可以在实现分析基元的转换阶段之前保留极大的自由度,这对于优化至关重要。
c. 设计
设计是产生可将分析构造映射到运行时间环境中的策略和机制的过程。其概念与分析不同,大部分初步设计工作可以在与分析活动无关的情况下进行。
d. 转换
转换是用设计策略将每一个要分析域的UML模型映射到实现的过程。设计分两个阶段进行:
结构设计:识别系统的执行单元(线程/任务/进程),将其分配至处理器,并将域分配至单元。
机械设计:开发将分析映射到实现的详细模式(用模板描述),并建立基本机制以支持这一实现。
测试仪器与源代码调试器相似
由于UML模型表示的是完整的可执行系统模型,因而可自动转换成实现。对模型应用一套转换法则,其情况与编译器转换高级编程语言相似。
语言编译器和模型编译器的相似之处在于:模型编译器可在所生成的代码中加入测试仪器代码,正如语言编译器可以为可执行程序加入符号表和调试信息一样。为两种编译器加入测试仪器可以使开发者在开发的同一抽象级进行测试和调试。高级语言的开发人员大都不愿在调试应用程序时查看汇编或机器代码。类似地,对于UML模型,开发者也希望在模型的较高抽象级进行调试,而不愿调试实现代码。
除程序代码外,转换方法还要利用UML模型信息来生成代码,以支持测试。测试仪器代码并未给软件增加额外的功能,只是提高了可测试性。测试仪器只提供对测试的支持,但在软件正常运行时却不能使用。
由于模型转换期间加入的测试仪器仅遵循UML模型执行语义,因而它可提供适用于任何应用的通用测试工具。可以在编译时将测试仪器分离出来,也可以在没有测试仪器的情况下重新生成代码,情况与编译器处理调试信息类似。
在人工实现的系统中,所需的测试仪器级别依赖于应用的复杂度、测试方法、目标环境、可用内存和其它工具的支持,以及可用的时间等。因此,必须权衡这些因素以按期推出高质量的产品。在UML模型中,必须识别重要的数据值、属性、输入和控制点。对每一项都应加入相应的测试仪器访问接口。下文将叙述如何利用转换方法添加测试仪器,其原理对手工实现同样适用。
使用测试仪器的应用结构
UML测试结构分成两个部分,即动态验证用户接口(DVUI)和测试仪器代理程序。DVUI负责为用户显示信息并接受用户命令,可由自动化批接口(batch interface)代替。
测试仪器代理程序是应用系统和DVUI之间的接口。代理程序和DVUI间的通讯机制可能是任何协议,如TCP/IP、RS-232等。代理程序通过所生成的测试仪器代码汇集信息和通信。它与测试仪器相连,以设置和获取实例数据,并支持DVUI关于中断和踪迹点的通知。它还与事件处理相连,可提供执行控制、系统激励、中断点和步进等功能。
数据测试仪器代码是在转换过程添加到应用系统的,用以监视和更新目标实例的数量、属性值、事件队列数量、事件数据项值和服务参数。
数据访问
在集成测试和调试期间,评价系统状态是一个重要步骤。由于系统状态分布在很多类实例中,DVUI应能获取系统的所有实例及属性值。
比较直接的方法是列出系统中实例的清单。每个类必须包括一些结构来保存该类的实例,通常是链接清单。在构造器(constructor)内,新的实例加入清单,而在解构器(destructor)内,实例则从清单中删除。
为了查看实例的状态,实例还可支持ASCII串行化,与“Java toString()”方法类似。“toString()”方法将每一实例的属性值和与之有关的实例放入字串中。然后,字串就由应用的代理程序传送至DVUI进行显示。为了设置实例的数据,还要支持“fromString()”方法以打开数据并正确地进行转换为属性赋值。
实例状态机的状态在调试过程中也应引起注意,因为它代表了系统控制流程的一个方面。由于它可影响实例对事件的响应,所以很特殊。将状态机的当前状态与其它类属性分开也很有用。
动态行为
在系统执行过程中进行着很多活动:生成和删除实例并交换事件;生成和删除关联;调用域服务;设置并触发定时器。所有这些活动都应在集成测试中注意。根据实现的不同,每个活动都会触发交互的中断点或踪迹输出。
DVUI应能将中断状态发送给代理程序。中断可让DVUI在执行过程中交互式地浏览该点的系统状态,并在必要时调整实例和属性。踪迹点也可由DVUI设置。在踪迹点,描述触发活动的信息传送给DVUI并存在日志文件中。踪迹点是在高层追踪系统执行情况的好方法。它们甚至可以后处理,以生成描述执行情境的序列图,也可用于回归测试。
中断和踪迹点控制可以通过发布-订阅模式实现。代理程序可支持所有类型的应用执行活动的注册。DVUI可以订阅这些活动,如某一特定实例的生成或状态机变换的初始化。当活动发生时,应用代码内的测试仪器通知代理程序,代理程序再通知订阅者执行相应的动作、中断或踪迹。
用通信状态机建模的系统实现包含一个事件队列,由对象从域外发送的事件放在队列中。事件环不断执行,将下一事件拉出队列,并传送至执行特定行为的目的实例。采用这一实现方法,事件环成为监控系统执行的中枢。
在事件环内加入测试仪器接口可监视经过的事件并查出接收实例应执行的下一状态。这些状态可与DVUI设置的中断和踪迹点状态相比较。
事件环中的测试仪器还应支持单事件步进模式。在这种模式下,DVUI命令应用执行一个动作,并在下一动作之前停止。
如果用分析级的执行语句描述UML模型动作和服务,可以用相似的方法编写代码,将分步执行事件扩展成分步执行语句。每一句执行语句跟随一条测试仪器指令,记录行数和局域变量值。当然,如果用手工操作将非常费时费力,但是通过转换则很容易实现。
测试的执行
这一部分将阐述在应用中使用测试仪器使测试和故障隔离更简单的不同方法。
a. 初始化
可以使用测试仪器将系统设置到已知的初始状态。用代理程序生成和初始化类实例,测试案例可以直接设置状态。这样就不必使用一个输入序列使它达到某种难以到达的状态,从而简化了这种状态下的测试。实例数据越过通信信道串行输入至代理程序,在这里生成新实例并将其初始化。
软复位功能还可使系统清零、用其它初始状态重新初始化,或运行其它测试案例。
b. 激励
激励通过测试仪器代理程序从DVUI施加到系统。事件可由DVUI初始化并通过测试仪器代理程序和事件队列传送到目标实例,此外,还要派生出事件和数据的一个编码方案。通过一些额外的编码,域服务还可以作为系统激励调用。这一机制与CORBA和RPC在跨越进程调用函数时所用的参数编码相似。
嵌入式系统中的一大难题是事件的时序或位序引起的故障重现性。通过控制事件在队列中传输的位序,可以测试和重现动作的排序。当然,除观察事件外,事件队列的接口还要允许事件的重新排序。
c. 数据采集
数据采集接口可用于考察测试案例之前和之后的状态。这对于确定和验证系统的最终状态(这是评价测试结果的一部分)特别有用。
测试仪器的另一优点是能捕捉真正的目标激励并在动态验证环境中重现。如果将测试仪器的某一部分留在最终产品中,它就可以像飞机上的“黑匣子”一样记录系统状态和输入。
d. 模仿
为了在动态验证过程中有效地单独测试域,必须完全了解域边缘的接口。针对域定义的测试案例通常会使用这一接口作为域的初始激励。测试案例和激励数据显然是专用的,但它使用的是由测试仪器提供的测试工具。
图1示出了一个测试驱动器,DVUI或其它与测试仪器代理程序相连的程序可模仿域的目标环境。作为测试一部分的类实例由驱动器进行初始化。测试驱动器施加测试激励并捕捉响应,还可通过捕获服务调用和替代返回值模仿其它域的响应。从被测域的角度讲,调用是一种输出,测试驱动器的响应能为被测域提供更多输入。测试驱动器利用被测域中的测试仪器捕获和替代消息。
从单个域到系统测试
这一测试方案可从单个域扩展到多个域的集合,并直至系统测试(图2)。利用测试驱动器模仿被测域的环境,单独域首先被隔离测试。然后,通过域服务的集合将多个域结合起来进行测试,测试驱动器仍然模仿被测域的环境。域之间以服务调用的形式相互联系的假设现在得到了验证。对测试边界间的接口和数据流应有充分的认识。
由于可以确信低级域中很少出现问题,可以在这些域中禁用测试支持仪器,以减少检测点的数目及提高吞吐量。一旦发现问题,可以重新起用测试支持,以收集个别测试案例的更多数据。
潜在的利用价值
在实现UML模型的过程中使用测试仪器,使得模型可在开发的测试阶段得以利用。开发者可以在模型级编写测试案例、执行和调试模型。在理想情况下,测试仪器应能在分析级提供充分的调试环境,应能对实例、事件和数据进行完全的互动操作,也应有主动激励系统的方法,而不仅仅是观察系统。测试仪器在UML建模阶段可以充分访问系统,因此可提高嵌入式系统的透明度,使集成测试更具可观察性、可控性和可测性。增强的可观察性可以探测到内部错误状态,而不会将错误传送到输出端。这样可将错误在离故障点更近的地方隔离出来,从而简化测试案例的开发,并缩短调试时间。
一旦出现可充分互动的分析调试器,就有可能通过增加批处理功能进一步提高质量和生产率。这种批处理能力可以进行自动回归测试、自动采集故障测试案例的数据或应用随机的测试和事件队列获取更多的分析结果。
作者简介:
Gregory Eakman是Pathfinder Solutions公司的首席顾问,现在波士顿大学攻读基于模型的软件自动化测试博士学位。他已成功地将基于模型的软件工程运用到不同的应用中。可通过grege@pathfindersol.com与其联系。
适用于目标管理结构的统一建模语言UML
统一建模语言(UML)对软件系统进行详细说明、形象化处理、架构建立和文挡管理的语言,也适用于业务建模和其他非软件系统。UML是在大型和复杂系统建模过程中形成的行之有效的最佳工程语言的集合。
目前,下列公司推出支持UML语言的产品:Rational Software、Microsoft、Hewlett-Packard、Oracle、Sterling Software、MCI Systemhouse、Unisys、ICON Computing、IntelliCorp、i-Logix、IBM、ObjecTime、Platinum Technology、Ptech、Taskon、Reich Technologies 以及Softeam。
在构造或创新面向工业控制的软件系统之前,建立软件模型恰如为大型建筑规划蓝图一样。
优良的模型不仅对于项目组之间的沟通非常重要,而且能够确保结构的坚固性。随着系统的复杂性不断增加,建模技术的重要性也日益增加。影响项目成败的原因很多,但是严格的建模语言标准是项目开发成功所必需的条件之一。
在UML诞生之前,实际上并没有明确的主导建模语言。用户要选择在表达能力上差异很小的多种相似的建模语言。大部分建模语言具备共同的表达符号,尽管它们之间存在细微的差异。
这些差异并没有提升建摸语言的能力,相反,它妨碍用户采用形象化的建模语言。
UML之所以适用于目标管理结构OMG(Object Management Architecture),是因为它通过标准化的技术支持与互操作性和便携性有关的基本设计任务。
UML建立在OMT、Booch、OSSE和其它重要的建模语言的基础之上,只要掌握OMT、Booch和OSSE三种语言之一,经过短期培训就能够掌握UML。UML的主要好处在于:确保软件开发项目的成功;不必因项目或组织变更而重新接受培训和重新选择设计工具,降低成本;为工具、过程和域的集成提供了一个环境;让开发工程师能集中精力考虑设计问题。
参考文献
Pathfinder Solutions, “MBSE Software Engineering Process”, February 2000: www.pathfindersol.com/download.html.
Pressman, Roger S.,“Software Engineering-A Practitioner''s Approach”, 3rd Edition. New York: McGraw-Hill, 1992.
Object Management Group, “The Unified Modeling Language" Specfication”, November 1999: www.omg.org.