具体实施方式
贯穿说明书和权利要求书,以下术语采用此处明确相关联的意义,除非上下文明确地另外指明。此处所使用的术语和接口规范并非表示应当用来书写特定对象或方法的特定语言。相反,使用这些术语和接口规范来描述接口或对象的功能和内容,如函数名、输入、输出、返回值、以及要使用接口来执行什么操作(或要由对象执行什么操作)。
说明性操作环境
参考图1,用于实现本发明的一个示例性系统包括诸如计算装置100的计算装置。在十分基本的配置中,计算装置100通常包括至少一个处理单元102和系统存储器104。根据计算装置的确切配置和类型,系统存储器104可以是易失性(如RAM)、非易失性(如ROM、闪存等)或两者的某一组合。系统存储器104通常包括操作系统105、一个或多个应用程序106,并且可包括程序数据107。在一个实施例中,应用程序106包括文字处理应用程序120,它进一步包括ML编辑器122。这一基本配置在图1中由虚线108内的组件示出。
计算装置100可具有另外的特征或功能。例如,计算装置100也可包括另外的数据存储设备(可移动和/或不可移动),如磁盘、光盘或磁带。这类另外的存储在图1中由可移动存储109和不可移动存储110示出。计算机存储介质可包括以用于储存如计算机可读指令、数据结构、程序模块或其它数据等信息的任一方法和技术实现的易失性和非易失性、可移动和不可移动介质。系统存储器104、可移动存储109和不可移动存储110都是计算机存储介质的示例。计算机存储介质包括但不限于,RAM、ROM、EEPROM、闪存或其它存储器技术、CD-ROM、数字多功能盘(DVD)或其它光存储、磁盒、磁带、磁盘存储或其它磁存储设备、或可以用来储存期望的信息并可由计算装置100访问的任一其它介质。任一这类计算机存储介质可以是装置100的一部分。计算装置100也可具有(多个)输入设备112,如键盘、鼠标、输入笔、语音输入设备、触摸输入设备等等。也可包括(多个)输出设备114,如显示器、扬声器、打印机等等。这些设备在本领域中是众所周知的,无需在此详细描述。
计算装置100也包含允许装置如通过网络与其它计算装置118进行通信的通信连接116。通信连接116是通信介质的一个示例。通信介质通常可以用诸如载波或其它传输机制等已调制数据信号中的计算机可读指令、数据结构、程序模块或其它数据实施,并包括任一信息传送介质。术语“已调制数据信号”指以对信号中的信息进行编码的方式设置或改变其一个或多个特征的信号。作为示例而非局限,通信介质包括有线介质,如有线网络或直接连线连接,以及无线介质,如声学、RF、红外和其它无线介质。本发明使用的术语计算机可读介质包括存储介质和通信介质两者。
本发明针对的是可通过属性来控制的测试实例继承行为。从其导出测试对象的基础测试类对于减少测试代码和管理是有用的。例如,基础测试类及其导出的对象可用于实现在整个测试类集之间公用的步骤(如,启动要被测试的软件片断并令其进入某一阶段)。继承的原理简化了当例如基类被修改时对测试软件的管理。当基类被修改时,从该基类导出的所有测试类被自动修改。因此,当改变对于修改(例如软件启动方式)为必需时,只要修改一个项(而非每一测试)。如下文所讨论的,可使用选择性的继承来允许使用继承的方法正确地执行测试。
尽管继承是非常有用的,然而不需要对于从该基础测试类导出的每一方法强制继承。依照本发明,对属性提供了一种机制用于选择是否应当应用继承。使用测试实例继承行为(可通过属性来控制)允许使用继承的方法正确地执行测试。属性定义的继承提供了基于类的分层结构对方法进行排序的方式(其中,排序由存在的属性确定)。例如,以递归的方式,设置步骤首先在基类上运行,然后在子类上运行,依此类推(而非首先在子类上运行,然后在基类上运行,或甚至是随机的方式)。如参考图2所描述的,执行引擎确定应当继承哪些方法,以及执行这些方法的顺序。
用于说明测试实例继承的伪代码的一个实例给出如下:
类TestEnvironmentBase(测试环境基)
Setup(设置)方法
Teardown(拆卸)方法
类ApplicationSpecificTest(应用专用测试):从TestEnvironmentBase
继承
Setup(设置)方法
Test(测试)方法
Teardown(拆卸)方法
对于测试类ApplicationSpecificTest(应用专用测试)将运行的Test(测试)方法以它们应当运行的顺序列出:
TestEnvironmentBase.Setup方法
ApplicationSpecificTest.Setup方法
ApplicationSpecificTest.Test方法
ApplicationSpecificTest.Teardown方法
TestEnvironmentBase.Teardown方法
在上述示例中,缩进表示类的分层结构,并示出了对给定类和子类的方法的继承原理。例如,ApplicationSpecificTest.Setup从TestEnvironmentBase.Setup类继承(除非测试作者另外选择,在这一情况下,测试作者可指定该方法不应当被继承)。
因此,测试作者可书写从基类导出的方法,并如所需要的选择性地向子类应用继承原理。提取引擎依照类的分层结构对方法排序,这确定了执行顺序。方法的排序可通过使用属性内定义的比较函数来完成,其结果是属性本身可用于确定类的分层结构。修改基类自动修改其子类,除非属性规定继承被“关闭”。
另外,可继承修改测试方法的状态的属性。例如,可继承规定某一异常是继承的属性(在否定测试(negative testing)的情况下)。这一异常被继承,并将测试方法结果状态从“失败”修改为“通过”,如果测试方法抛出异常,则这覆盖了默认的失败实例。
测试自动化系统体系结构
图2所示是用于实施本发明的示例性环境的框图。图2所示的示例性环境是包括测试装具模块210、测试运行库220和测试实例脚本230的测试自动化系统200。
测试运行库
在一个实施例中,测试运行库220是从测试装具模块中抽象出测试实例的知识的对象的集合。测试运行库220通常包括测试服务提供者对象221、提取引擎222、属性223和测试方法执行程序224。测试运行库220可由不同的装具模块用于提供对特定类型的测试实例格式的一致支持。通过扩展,测试装具模块可使用不同的测试运行库来支持不同类型的测试实例格式。测试装具模块通常确定对特定的测试实例格式使用哪一测试运行库。
测试实例提取是通过一专用的提取对象(从测试服务提供者对象221中获取,下文描述)来完成的,而调用是由向测试装具模块返回广义结果的对象(测试方法执行程序224,也在下文描述)执行的。测试方法执行程序使用广义接口对属性求值,以控制方法的执行。
不需要测试装具模块来例如对属性求值、确定测试方法应当执行的顺序、为方法调用构建自变量列表等等。测试装具模块通常不能直接访问执行那些任务所需的这一信息,这有助于确保不同的测试装具模块之间的更一致的测试执行。所有的依赖测试装具模块的功能(记入日志、“遥控”等等)应当是由测试装具模块实现的、由接口来描述的、并且储存在测试服务提供者对象中,以在测试执行期间使用的对象。因此,可创建容易地能够在不同的测试运行库之间切换,而不需要对测试装具模块代码的改变的测试装具模块。
测试服务
测试服务提供者对象221由测试装具模块用于检索提取引擎,并由测试实例脚本用于检索测试装具模块实现的功能的对象(包括诸如记入日志、同步等测试服务)。
测试服务提供者对象通常提供用于方便访问测试服务的方法。AddService(添加服务)方法被调用以储存对实现测试服务的对象的引用。测试服务对象应当实现一“广义”类型,它方便了不同的装具模块能够提供的一组标准服务。传入的对象应当实现属性或测试方法所需的功能。该方法应当由测试装具模块对测试装具模块提供的每一服务调用,它一般在执行测试方法之前完成。
GetService(获取服务)方法通常被调用以检索一种类型的测试服务对象。传入的类型应当表示由测试服务对象实现的接口。如果找到,则返回实现该对象的对象。如果未找到对象,则返回表示这一结果的值(如,返回空)。如果测试或属性需要不存在的测试服务,则所执行的测试方法应当失败。
AddDefaultServices(添加默认服务)保护的方法通常由构造函数调用。它通常用于添加测试运行库默认地提供的任何测试服务,如提取引擎。
测试服务提供者对象数据和方法调用应当是静态的,使得由测试装具模块设置的数据集可在稍后的时间点由对测试服务提供者的其它调用来检索。对象通常是实现上述功能的类。该对象的构造函数通常用于调用AddDefaultServices方法。
提取引擎
提取引擎222用于对特定的测试实例脚本从测试装具模块的测试实例中检索测试方法的有序列表。通常在测试运行库只有一个提取引擎。
GetTestMethodWrappers(获取测试方法包装)方法用于对测试实例脚本检索测试方法的有序列表。(测试方法包装是测试方法执行程序的特定实现)。传入到该方法的参数表示保持该测试实例的容件(container)。该方法返回方法的有序列表。如果在提取测试实例时发生错误,则可“抛出”异常。如果没有找到任何测试实例或测试方法,则通常返回空列表。空列表通常由测试装具模块作为失败来处理。如果需要将额外的数据传入提取引擎,则它可由TestHarnessDataProvider(测试装具模块数据提供者)测试服务通过TestServicesProvider(测试服务提供者)对象来提供。
提取引擎对象中存在的大部分功能在不同的运行库都是相同的;从一个运行库到另一运行库显著改变的唯一细节是使用了哪些属性。应当创建对象以便于新提取引擎和运行库的容易创建。该类通常实现以下函数:
TypeIsATestCase(类型是测试实例),如果传入的类型是测试实例,则返回“真”,否则返回“假”。这一函数通过查找测试实例类属性来检查类型上存在的属性,以确定类型是否为测试实例。
MethodIsATestMethod(方法是测试方法),如果传入的方法是测试方法则返回“真”,否则返回“假”。该函数通过查找执行属性来检查类型上存在的属性,以确定类型是否为测试方法。
GetMethodWrappersFromType(从类型获取方法包装),它收集具有执行属性的类型上所有相关的公有非静态方法,并返回这些方法作为MethodWrappers(下文讨论)的有序列表。在使用.NET环境的一个实施例中,列表使用构建到.Net数组中的分类功能来排序,使得MethodWrapper调用一比较例程来对列表进行排序。如果不能创建该类型的实例,则该方法失败,并返回空列表。
提取引擎可使用测试服务来检索可用于修改测试提取的信息。该信息可储存在诸如XML文件等文件中,那是按照为储存测试提取信息而定义的模式。如果由测试服务提供的数据不涉及依照该模式的XML文件,则可忽略修改数据。
提取引擎通常加载所指定的所有提取修改器XML文件。XML文件的内容是,例如,放入两个“存储桶(bucket)”中:测试包括和测试排除。如果两个存储桶都为空,则提取引擎应当包括所有的测试。这一情况等效于没有提取修改符xml文件,或不能检索提取引擎数据源测试服务。如果仅排除存储桶为空,则提取引擎应当包括所有的测试。如果仅包括存储桶为空,则提取引擎应当包括所有的测试,并排除在排除存储桶中所列出的测试。如果两个存储桶都有数据,则提取引擎应当包括在包括存储桶中未在排除存储桶中列出的测试(使得排除列表具有对包括列表的控制权限)。
测试方法执行程序
测试方法执行程序224用于执行测试方法,而无需调用者具有关于该方法或其属性的预先知识。Invoke(调用)方法被调用来执行测试方法。返回保留操作结果的对象(通过、失败、跳过等等)。Invoke方法负责处理与方法相关联的属性,并为所调用的方法创建参数列表(如果需要的话)。执行通常由与测试方法相关联的属性修改。
Abort(中止)方法可被调用以中止当前正在执行的测试方法。中止通常导致当前正在运行的Invoke方法返回。在执行了中止之后,没有进一步的测试可被正常地运行。
CompareTo(比较)方法被调用以将两个测试方法包装(Test Method Wrapper)进行比较。如果返回的结果小于零,则它表明该方法应当在另一测试方法包装(与其进行比较的方法)之前执行。如果返回的结果等于零,则它表明执行这两个方法的顺序无关紧要。如果返回的结果大于零,则它表明该方法应当在另一测试方法包装之后执行。
GetMethodAttributes(获取方法属性)方法被调用以检索与从公用基础方法属性类导出的测试方法相关联的属性的有序列表。该属性的有序类列表由测试方法包装在若干位置使用。例如,Invoke方法(如上文所描述的)使用有序列表来以正确的顺序对属性求值。同样,GetMethodAttributes可用于将一个方法包装与另一个进行比较。诸如“Get AttributesDescription(获取属性描述)”等调用使用属性的有序列表来创建与该方法相关联的属性的串描述。
测试方法包装具有若干性质,它们可通过诸如调用“获取Description(描述)”和“Get Name(获取名字)”来检索,以供测试装具模块使用。性质从方法名和从其定义方法的类生成。在需要时可添加其它性质。注意,这些性质不需要装具模块知道关于所查询的测试方法的任何知识,并且可在不需要对现有测试装具模块的修改的情况下添加额外的性质。
MethodResult(方法结果)对象用于将测试方法包装的结果传递到测试装具模块。由于测试装具模块不必具有所调用的方法的预先知识,因此以抽象的形式来表达结果。
对象通常需要从试图执行方法来表达三个可能的结果:通过、跳过或失败。“通过”通常表明方法在没有错误的情况下完成了执行(例如,方法没有将任何失败记入日志、测试方法未抛出异常、以及没有一个属性有错误)。“错误”表明方法失败(例如,测试方法表明失败,或属性表明失败)。“跳过”表明方法被跳过而非被执行(例如,属性指定测试方法应当仅在服务器上运行,但是测试正在客户机机器上运行;在这一情况下,方法将被跳过)。
MethodResult对象也可包含可任选的消息,如结果消息和/或错误消息。结果消息可以是方法结果的人类可读的描述。在成功地执行了一个方法之后,它将被保留为空,或者它可包含记录的遍数。对于其中出现了错误的已完成方法,可包括对该错误的文本描述,而错误消息可包含该错误的细节。
当由属性“抛出”异常时,TestConditionException类可用于传送修改的方法状态。例如,直接映射到方法状态的三个导出类包括TestSkipException(测试跳过异常)、TestSucceededException(测试成功异常)和TestErrorException(测试错误异常)。
属性223通常用于修改和控制测试的执行。测试依照测试实例脚本来执行,它可通过使用属性来定义。可使用至少三种基本类型的属性:类级属性、方法级属性和参数级属性。
测试类级属性是可任选的,并可用于修改例示的对象状态,使得测试提取可被跳过或导致对指示的类型多次执行。预提取和后提取方法通常用于修改例示的对象状态。测试类属性允许测试实例脚本中的这类变化被实现。
方法级属性能够修改方法参数和方法执行。在一个实施例中,方法级属性包括执行属性和补充属性。这两种属性都具有预调用和后调用方法。对方法级属性求值的顺序由一顺序性质确定,它在书写属性时定义;然而,属性通常没有关于可能存在哪些其它属性的预先知识。每一阶段的执行修改可由基于优先级的状态系统处理一返回具有最高优先级的状态的属性通常用于确定如何修改执行。
执行属性用于将方法标记为测试方法。没有执行属性的方法通常不包括在测试中。执行属性的核心责任是建立高级顺序以测试并评估方法结果。方法应当没有一个以上的执行属性。当存在一个以上执行属性时,提取和执行行为通常是未定义的。执行属性的示例包括“Setup(设置)”、“Step(步进)”和“Teardown(拆卸)”属性。
补充属性执行补充行动以修改测试方法的执行。补充属性的核心责任是执行对测试的执行必需的次要任务。它们通常不用于标记高级顺序。方法可具有任意数量的补充属性。补充属性的示例包括“WaitFor(等待)”和“Target(目标)”属性。
参数级属性可选地用于修改对方法的参数输入,并在执行了方法之后修改对象的状态(如,上下文状态)。当使用了方法级属性时,参数级属性通常不用于改变测试的执行。然而,如果抛出了异常,则测试方法响应于该异常立即失败。在一个实施例中,对每一参数只有一个参数级属性;具有一个以上参数级属性的行为保留未定义。参数级属性在调用方法前对方法级属性求值之后被求值,并在调用方法之后方法级属性被求值之前被求值。参数级属性的一个示例包括“ContextMapping(上下文映射)”属性。
MethodState(方法状态)对象由方法属性用于控制测试方法的执行。由于可向一个测试方法分配多个属性(并且由于每一属性可潜在地改变测试方法的执行),每一属性可与MethodState对象进行通信,以确保测试方法的一致执行。
MethodState对象可包括涉及执行状态、消息、错误代码和状态覆盖优先级的信息。执行状态包括关于方法如何中止(如,跳过、通过、失败)、状态是否被准许来改变、以及方法是否应当被执行的信息。消息可用于可任选地呈现指示测试方法为何处于特定状态的文本。错误代码可用于指示测试方法包装可能在执行测试方法时遇到的错误的细节。状态覆盖优先级字段可用于通过允许仅当新状态具有高于现有状态的优先级时改变执行状态、消息和错误代码,来提高测试方法执行的一致性。
测试方法包装(224)执行测试方法,直到达到终止状态。当达到终止状态时,从最终的MethodState对象构造MethodResult对象。
执行属性负责对从方法调用获取的结果进行语法分析。为确定方法是通过还是失败,可监视日志中的通过和失败条目。如果任何失败被记入日志,则该方法可能失败。如果没有通过或失败被记入日志,则方法也可能失败。如果从测试方法或任何属性抛出异常,则方法也可能失败。否则,方法可被认为已(成功地)通过。
测试示例脚本
在一个实施例中,测试示例脚本230是对特定的测试示例协调测试方法的执行的对象集合。由于由测试方法执行程序(224)提供并通过它的接口,可在没有测试装具模块的预先知识的情况下书写测试方法。
测试示例脚本230通常包括测试方法231和其它方法和数据232。测试方法通过使用运行库对象(包括测试方法执行程序)而非通过查询特定的测试装具模块,来访问测试装具模块对象。
测试装具模块
在一个实施例中,测试装具模块210是协调测试实例的执行并提供各种测试服务的对象的集合。测试装具模块210通常包括UI(用户界面)211、执行引擎212、上下文对象213和记入日志对象214。用于添加的功能的目的的测试装具模块可包括诸如自动化系统接口等其它对象。
执行引擎(212)负责使用测试运行库(220)加载并执行测试实例脚本。图3示出了依照本发明的各方面的执行引擎的过程流300。在开始块之后,过程移动到块310,加载测试运行库。在用.Net书写测试运行库的一个实施例中,测试运行库组件和测试实例脚本组件被加载到AppDomain(应用程序域)中。测试装具模块可显示诸如加载的.Net运行库的版本,或正使用的测试运行库的版本等信息。
在块320,一个或多个测试实例被加载/编译到存储器。测试实例可被预编译并加载到存储器,或加载到存储器然后被编译。测试装具模块可显示关于测试实例脚本以及测试实例脚本是否被成功地加载和/或编译的信息。
在块330继续,获取提取引擎。提取引擎通过首先检索测试服务提供者对象、(221)来获取。下一步,从测试方法执行程序(220)确定基础提取引擎的类型。调用测试服务提供者对象上的静态GetService(获取服务)函数(将基础提取引擎的类型传递到测试服务提供者对象),以接收对提取引擎的引用。
在块340,测试装具模块功能被添加到测试服务提供者。测试服务提供者上的AddService(添加服务)方法用于向测试服务提供者添加在测试装具模块上实现的测试服务。测试服务包括实现诸如重启机制、记入日志、上下文等的各种接口的对象。如果期望将数据传递到提取引擎(例如,指定某一方法应当被包括或跳过的XML文件),则可使用实现测试装具模块数据提供者接口的测试服务对象。
在块350,提取引擎用于获取测试实例步骤。在块330获取的提取引擎用于调用提取引擎的GetTestMethodWrappers(获取测试方法包装)方法、向方法传递保留测试实例的AppDomain。通常返回一个TestMethodWrappers(测试方法包装)的数组。
方法包装的数组通常包含应当以它们在数组中存在的顺序执行的测试行动的列表。(提取引擎通常负责对数组进行排序)。
在块360,以列出方法的顺序执行在块350检索到的方法列表。每一方法通过调用Invoke(调用)方法来执行。调用方法通常返回关于测试行动结果的细节。细节可包括成功/失败/跳过结果,以及附加细节。结果的细节可以是,例如,被记入日志,或用于更新UI。
图4示出了依照本发明的各方面的执行引擎的过程流400。对于每一测试行动,重复过程400。执行引擎调用InvokeInstanceMethod(调用实例方法)来例示特定测试行动的执行。InvokeInstanceMethod调用InstanceMethod(实例方法)来调用特定的测试行动。InstanceMethod进而调用MethodWrapper(即,示例测试方法执行程序)来调用特定的测试行动。
方法包装对特定测试行动的属性进行求值并执行(预调用)。方法包装接下来依照所求值/执行的测试方法调用测试方法。在执行了测试方法之后,属性再一次被求值/执行(后调用)。方法包装解释后调用属性求值的结果,并返回表示该结果的值。ActionResult(行动结果)被传递到InstanceMethod,并进而被传递到InvokeInstanceMethod。InvokeInstanceMethod评估返回值并将结果传递到执行引擎。
作为对本发明的进一步说明,提供伪代码清单如下:ExtractionEngine.GetTestMethodWrappers
TestMethodWrappers [] methodWrappers;
For(AppDomain中的每一组件)
{
For(组件中的每一类型)
{
If(TypeIsATestCase(type))
{
methodWrappers+=GetMethodWrappersFromType(type)
}
}
}
Return methodWrappersExtractionEngine.GetMethodWrappersFromType
TestMethodWrappers [] allMethodWrappers
ExtractionState curState=无效状态
Do
{
TestMethodWrappers [] instanceMethodWrappers
curState.Reset()
if(类型是可创建的)
Object testObject=new类型;
Else
Return空列表;
For(类型上的每一属性)
{
If(属性是测试示例属性的种类)
curState=Attribute.PreExtract(testObject)
}
If(curState=终止状态)
break;
MethodInfo [] potentialTestMethods=type.GetAllPublicMethods()
For(potentialTestMethods中的每一方法)
{
If(MethodIsATestMethod(method))
{
TestMethodWrapper wrapper=new Wrapper(obj,method)
If(!wrapper.GetMethodAttributes[0].Inherit)
{
If(method.definingType==type)
instanceMethodWrappers+=wrapper
}
Else
instanceMethodWrappers+=wrapper
}
}
For(类型上的每一属性)
{
If(属性是测试实例属性的种类)
curState=Attribute.PostExtract()
}
instanceMethodWrappers.Sort()
allMethodWrappers+=instanceMethodWrappers
}
While(curState!=终止状态)//无效状态或跳过
Return allMethodWrappers;TestMethodWrapper.Invoke
MethodState curState=无效状态
Do
{
curState.Reset()
TestMethodAttribute [] methodAttributes=
GetMethodAttributes()
methodAttributes.Reverse()
//对所有属性预调用
Try
{
//方法级属性
For(methodAttributes中的每一属性)
{
MethodState newState=attribute.PreInvoke(...)
if(newState.execstate!=nochange &&
newState.priority>curState.priority)
{
curState=newState
}
}
}
Catch(UnexpectedCondition)
{
curState=new MethodState(UnexpectedCondition);
}
Catch(e)
{
//如果属性抛出异常,调用失败
curState=new MethodState(e);
break;
}
//如果需要的话评估状态和停止调用
//-状态必需在检查参数级属性之前评估
if(curState.execstate==终止状态)
{
break;
}
Try
{
//参数级状态属性-这些属性不能修改状态,
//但是如果它们抛出,则调用失败
For(方法上的每一参数)
{
For(参数上的每一属性)
{
If(attribute is kind of
MotifParamAttribute)
Attribute.PreInvoke(...)
}
}
}
Catch(e)
{
curState=new MethodState(e)
break;
}
//执行测试方法
Object result;
Try
{
result=method.invoke(...)
}
Catch(e)
{
//注意如果方法抛出,则测试不自动失败;
//相反,异常作为返回值来处理,供执行属性处理
result=e
}
//所有属性上的后调用
//如果任一属性抛出,调用失败
curState.Reset()
Try
{
//对参数级属性的后调用
For(方法上的每一参数)
{
For(参数上的每一属性)
{
If(attribute is kind of
MotifParamAttribute)
Attribute.PostInvoke(...)
}
}
//所有方法级属性上的后调用
For(methodAttributes中的每一属性)
{
MethodState newState=attrib.PostInvoke(...)
If(newState.execstate!=nochange &&
newState.priority>curState.priority)
{
curState=newState;
}
}
}
Catch(UnexpectedCondition)
{
curState=new MethodState(UnexpectedCondition);
}
Catch(e)
{
curState=new MethodState(e)
break;
}
}
While(curState!=终止状态)//成功、错误、跳过、无效状态
将最终状态记入日志
Return MethodResult(curState);ExecutionAttribute.PreInvoke
logManager=TestServiceProvider.GetService(LogManager)
If(logManager==null)
Throw UnexpectedErrorCondition(″LogManager object notfound″)
logManager.AddListener(us)
Return MethodState(执行,低优先级)ExecutionAttribute.PostInvoke
LogManager.RemoveListener(us)
If(result is kind of exception)
{
Return MethodState(失败,低优先级)
}
Else If(failedCount>0)
{
Return MethodState(失败,中优先级)
}
Else If(passedCount==0)
{
Return MethodState(失败,中优先级)
}
Else//passedCount>0,failedCount=0
{
Return MethodState(通过,中优先级)
}TargetAttribute.PreInvoke
If(Context.CurrentMachine不在RunOnMachinesList中)
Throw UnexpectedSkipCondition(Context.CurrentMachine+
″not in″+
RunOnMachinesList)
Else
Return MethodState(nochange)VariationsAttribute.PostInvoke
currentVariation++;
If(currentVariation<totalVariations)
Return MethodState(执行,高优先级)
Else
Return MethodState(通过/失败,中优先级)
以上说明书、实例和数据提供了本发明的组成部分的制造和使用的完整描述。由于可作出本发明的许多实施例而不脱离本发明的范围,本发明驻留在所附权利要求书中。