一个人不应该依附在其他人身上,一个人应该首先自力更生。你应该自己能够独立,能够安顿你自己,那你就不会害怕了。你爱你自己的话,别人不能不爱你吧。

发布新日志

  • 用例建模指南(转载)

    2007-08-08 09:04:54

    原文

    用例建模指南

    作者:傅纯一 选自: IBM
    用例(Use Case)是一种描述系统需求的方法,使用用例的方法来描述系统需求的过程就是用例建模。用例方法最早是由Iva Jackboson博士提出的,后来被综合到UML规范之中,成为一种标准化的需求表述体系。用例的使用在RUP中被推崇备至,整个RUP流程都被称作是"用例驱动"(Use-Case Driven)的,各种类型的开发活动包括项目管理、分析设计、测试、实现等都是以系统用例为主要输入工件,用例模型奠定了整个系统软件开发的基础。

    1. 什么是用例?
    在介始用例方法之前,我们首先来看一下传统的需求表述方式-"软件需求规约"(Software Requirement Specification)。传统的软件需求规约基本上采用的是功能分解的方式来描述系统功能,在这种表述方式中,系统功能被分解到各个系统功能模块中,我们通过描述细分的系统模块的功能来达到描述整个系统功能的目的。

    采用这种方法来描述系统需求,非常容易混淆需求和设计的界限,这样的表述实际上已经包含了部分的设计在内。由此常常导致这样的迷惑:系统需求应该详细到何种程度?一个极端就是需求可以详细到概要设计,因为这样的需求表述既包含了外部需求也包含了内部设计。在有些公司的开发流程中,这种需求被称为"内部需求",而对应于用户的原始要求则被称之为"外部需求"。

    功能分解方法的另一个缺点是这种方法分割了各项系统功能的应用环境,从各项功能项入手,你很难了解到这些功能项是如何相互关联来实现一个完成的系统服务的。所以在传统的SRS文档中,我们往往需要另外一些章节来描述系统的整体结构及各部分之间的相互关联,这些内容使得SRS需求更象是一个设计文档。

    1.1 参与者和用例
    从用户的角度来看,他们并不想了解系统的内部结构和设计,他们所关心的是系统所能提供的服务,也就是被开发出来的系统将是如何被使用的,这就用例方法的基本思想。用例模型主要由以下模型元素构成:

    • 参与者(Actor)
      参与者是指存在于被定义系统外部并与该系统发生交互的人或其他系统,他们代表的是系统的使用者或使用环境。
    • 用例(Use Case)
      用例用于表示系统所提供的服务,它定义了系统是如何被参与者所使用的,它描述的是参与者为了使用系统所提供的某一完整功能而与系统之间发生的一段对话。
    • 通讯关联(Communication Association)
      通讯关联用于表示参与者和用例之间的对应关系,它表示参与者使用了系统中的哪些服务(用例),或者说系统所提供的服务(用例)是被哪些参与者所使用的。

    这大三种模型元素在UML中的表述如下图所示。


    以银行自动提款机(ATM)为例,它的主要功能可以由下面的用例图来表示。ATM的主要使用者是银行客户,客户主要使用自动提款机来进行银行帐户的查询、提款和转帐交易。


    通讯关联表示的是参与者和用例之间的关系,箭头表示在这一关系中哪一方是对话的主动发起者,箭头所指方是对话的被动接受者;如果你不想强调对话中的主动与被动关系,可以使用不带箭头的关联实线。在参与者和用例之间的信息流不是由通讯关联来表示的,该信息流是缺省存在的(用例本身描述的就是参与者和系统之间的对话),并且信息流向是双向的,它与通讯关联箭头所指的方向亳无关系。

    1.2 用例的内容
    用例图使我们对系统的功能有了一个整体的认知,我们可以知道有哪些参与者会与系统发生交互,每一个参与者需要系统为它提供什么样的服务。用例描述的是参与者与系统之间的对话,但是这个对话的细节并没有在用例图中表述出来,针对每一个用例我们可以用事件流来描述这一对话的细节内容。如在ATM系统中的"提款"用例可以用事件流表述如下:

    提款-基本事件流

    1. 用户插入信用卡

    2. 输入密码

    3. 输入提款金额

    4. 提取现金

    5. 退出系统,取回信用卡

    但是这只描述了提款用例中最顺利的一种情况,作为一个实用的系统,我们还必须考虑可能发生的各种其他情况,如信用卡无效、输入密码错、用户帐号中的现金余额不够等,所有这些可能发生的各种情况(包括正常的和异常的)被称之为用例的场景(Scenario),场景也被称作是用例的实例(Instance)。在用例的各种场景中,最常见的场景是用基本流(Basic Flow)来描述的,其他的场景则是用备选流(Alternative Flow)来描述。对于ATM系统中的"提款"用例,我们可以得到如下一些备选流:

    提款-备选事件流

    备选流一:用户可以在基本流中的任何一步选择退出,转至基本流步骤5。

    备选流二:在基本流步骤1中,用户插入无效信用卡,系统显示错误并退出信用卡,用例结束。

    备选流三:在基本流步骤2中,用户输入错误密码,系统显示错误并提示用户重新输入密码,重新回到基本流步骤2;三次输入密码错误后,信用卡被系统没收,用例结束。

    通过基本流与备选流的组合,就可以将用例所有可能发生的各种场景全部描述清楚。我们在描述用例的事件流的时候,就是要尽可能地将所有可能的场景都描述出来,以保证需求的完备性。

    1.3 用例方法的优点
    用例方法完全是站在用户的角度上(从系统的外部)来描述系统的功能的。在用例方法中,我们把被定义系统看作是一个黑箱,我们并不关心系统内部是如何完成它所提供的功能的。用例方法首先描述了被定义系统有哪些外部使用者(抽象成为Actor),这些使用者与被定义系统发生交互;针对每一参与者,用例方法又描述了系统为这些参与者提供了什么样的服务(抽象成为Use Case),或者说系统是如何被这些参与者使用的。所以从用例图中,我们可以得到对于被定义系统的一个总体印象。

    与传统的功能分解方式相比,用例方法完全是从外部来定义系统的功能,它把需求与设计完全分离开来。在面向对象的分析设计方法中,用例模型主要用于表述系统的功能性需求,系统的设计主要由对象模型来记录表述。另外,用例定义了系统功能的使用环境与上下文,每一个用例描述的是一个完整的系统服务。用例方法比传统的SRS更易于被用户所理解,它可以作为开发人员和用户之间针对系统需求进行沟通的一个有效手段。

    在RUP中,用例被作为整个软件开发流程的基础,很多类型的开发活动都把用例作为一个主要的输入工件(Artifact),如项目管理、分析设计、测试等。根据用例来对目标系统进行测试,可以根据用例中所描述的环境和上下文来完整地测试一个系统服务,可以根据用例的各个场景(Scenario)来设计测试用例,完全地测试用例的各种场景可以保证测试的完备性。

    2. 建立用例模型
    使用用例的方法来描述系统的功能需求的过程就是用例建模,用例模型主要包括以下两部分内容:

    • 用例图(Use Case Diagram)
      确定系统中所包含的参与者、用例和两者之间的对应关系,用例图描述的是关于系统功能的一个概述。
    • 用例规约(Use Case Specification)
      针对每一个用例都应该有一个用例规约文档与之相对应,该文档描述用例的细节内容。

    在用例建模的过程中,我们建议的步聚是先找出参与者,再根据参与者确定每个参与者相关的用例,最后再细化每一个用例的用例规约。

    2.1 寻找参与者
    所谓的参与者是指所有存在于系统外部并与系统进行交互的人或其他系统。通俗地讲,参与者就是我们所要定义系统的使用者。寻找参与者可以从以下问题入手:

    • 系统开发完成之后,有哪些人会使用这个系统?
    • 系统需要从哪些人或其他系统中获得数据?
    • 系统会为哪些人或其他系统提供数据?
    • 系统会与哪些其他系统相关联?
    • 系统是由谁来维护和管理的?

    这些问题有助于我们抽象出系统的参与者。对于ATM机的例子,回答这些问题可以使我们找到更多的参与者:操作员负责维护和管理ATM机系统、ATM机也需要与后台服务器进行通讯以获得有关用户帐号的相关信息。


    2.1.1 系统边界决定了参与者

    参与者是由系统的边界所决定的,如果我们所要定义的系统边界仅限于ATM机本身,那么后台服务器就是一个外部的系统,可以抽象为一个参与者。


    如果我们所要定义的系统边界扩大至整个银行系统,ATM机和后台服务器都是整个银行系统的一部分,这时候后台服务器就不再被抽象成为一个参与者。


    值得注意的是,用例建模时不要将一些系统的组成结构作为参与者来进行抽象,如在ATM机系统中,打印机只是系统的一个组成部分,不应将它抽象成一个独立的参与者;在一个MIS管理系统中,数据库系统往往只作为系统的一个组成部分,一般不将其单独抽象成一个参与者。

    2.1.2 特殊的参与者――系统时钟

    有时候我们需要在系统内部定时地执行一些操作,如检测系统资源使用情况、定期地生成统计报表等等。从表面上看,这些操作并不是由外部的人或系统触发的,应该怎样用用例方法来表述这一类功能需求呢?对于这种情况,我们可以抽象出一个系统时钟或定时器参与者,利用该参与者来触发这一类定时操作。从逻辑上,这一参与者应该被理解成是系统外部的,由它来触发系统所提供的用例对话。


    2.2 确定用例
    找到参与者之后,我们就可以根据参与者来确定系统的用例,主要是看各参与者需要系统提供什么样的服务,或者说参与者是如何使用系统的。寻找用例可以从以下问题入手(针对每一个参与者):

    • 参与者为什么要使用该系统?
    • 参与者是否会在系统中创建、修改、删除、访问、存储数据?如果是的话,参与者又是如何来完成这些操作的?
    • 参与者是否会将外部的某些事件通知给该系统?
    • 系统是否会将内部的某些事件通知该参与者?

    综合以上所述,ATM系统的用例图可表示如下,


    在用例的抽取过程中,必须注意:用例必须是由某一个主角触发而产生的活动,即每个用例至少应该涉及一个主角。如果存在与主角不进行交互的用例,就可以考虑将其并入其他用例;或者是检查该用例相对应的参与者是否被遗漏,如果是,则补上该参与者。反之,每个参与者也必须至少涉及到一个用例,如果发现有不与任何用例相关联的参与者存在,就应该考虑该参与者是如何与系统发生对话的,或者由参与者确定一个新的用例,或者该参与者是一个多余的模型元素,应该将其删除。

    可视化建模的主要目的之一就是要增强团队的沟通,用例模型必须是易于理解的。用例建模往往是一个团队开发的过程,系统分析员在建模过程中必须注意参与者和用例的名称应该符合一定的命名约定,这样整个用例模型才能够符合一定的风格。如参与者的名称一般都是名词,用例名称一般都是动宾词组等。

    对于同一个系统,不同的人对于参与者和用例都可能有不同的抽象结果,因而得到不同的用例模型。我们需要在多个用例模型方案中选择一种"最佳"(或"较佳")的结果,一个好的用例模型应该能够容易被不同的涉众所理解,并且不同的涉众对于同一用例模型的理解应该是一致的。

    2.3 描述用例规约
    应该避免这样一种误解――认为由参与者和用例构成的用例图就是用例模型,用例图只是在总体上大致描述了系统所能提供的各种服务,让我们对于系统的功能有一个总体的认识。除此之外,我们还需要描述每一个有例的详细信息,这些信息包含在用例规约中,用例模型是由用例图和每一个用例的详细描述――用例规约所组成的。RUP中提供了用例规约的模板,每一个用例的用例规约都应该包含以下内容:

    • 简要说明 (Brief Descrīption)
      简要介绍该用例的作用和目的。
    • 事件流 (Flow of Event)
      包括基本流和备选流,事件流应该表示出所有的场景。
    • 用例场景 (Use-Case Scenario)
      包括成功场景和失败场景,场景主要是由基本流和备选流组合而成的。
    • 特殊需求 (Special Requirement)
      描述与该用例相关的非功能性需求(包括性能、可靠性、可用性和可扩展性等)和设计约束(所使用的操作系统、开发工具等)。
    • 前置条件 (Pre-Condition)
      执行用例之前系统必须所处的状态。
    • 后置条件 (Post-Condition)
      用例执行完毕后系统可能处于的一组状态。

    用例规约基本上是用文本方式来表述的,为了更加清晰地描述事件流,也可以选择使用状态图、活动图或序列图来辅助说明。只要有助于表达的简洁明了,就可以在用例中任意粘贴用户界面和流程的图形化显示方式,或是其他图形。如活动图有助于描述复杂的决策流程,状态转移图有助于描述与状态相关的系统行为,序列图适合于描述基于时间顺序的消息传递。

    2.3.1 基本流

    基本流描述的是该用例最正常的一种场景,在基本流中系统执行一系列活动步骤来响应参与者提出的服务请求。我们建议用以下格式来描述基本流:

    1) 每一个步骤都需要用数字编号以清楚地标明步骤的先后顺序。

    2) 用一句简短的标题来概括每一步骤的主要内容,这样阅读者可以通过浏览标题来快速地了解用例的主要步骤。在用例建模的早期,我们也只需要描述到事件流步骤标题这一层,以免过早地陷入到用例描述的细节中去。

    3) 当整个用例模型基本稳定之后,我们再针对每一步骤详细描述参与者和系统之间所发生的交互。建议采用双向(roundtrip)描述法来保证描述的完整性,即每一步骤都需要从正反两个方面来描述:(1)参与者向系统提交了什么信息;(2)对此系统有什么样的响应。具体例子请参见附录。

    在描述参与者和系统之间的信息交换时,需指出来回传递的具体信息。例如,只表述参与者输入了客户信息就不够明确,最好明确地说参与者输入了客户姓名和地址。通常可以利用词汇表让用例的复杂性保持在可控范围内,可以在词汇表中定义客户信息等内容,使用例不至于陷入过多的细节。

    2.3.2 备选流

    备选流负责描述用例执行过程中异常的或偶尔发生的一些情况,备选流和基本流的组合应该能够覆盖该用例所有可能发生的场景。在描述备选流时,应该包括以下几个要素:

    1) 起点:该备选流从事件流的哪一步开始;

    2) 条件:在什么条件下会触发该备选流;

    3) 动作:系统在该备选流下会采取哪些动作;

    4) 恢复:该备选流结束之后,该用例应如何继续执行。

    备选流的描述格式可以与基本流的格式一致,也需要编号并以标题概述其内容,编号前可以加以字母前缀A(Alternative)以示与基本流步骤相区别。

    2.3.3 用例场景

    用例在实际执行的时候会有很多的不同情况发生,称之为用例场景;也可以说场景是用例的实例,我们在描述用例的时候要覆盖所有的用例场景,否则就有可能导致需求的遗漏。在用例规约中,场景的描述可以由基本流和备选流的组合来表示。场景既可以帮助我们防止需求的遗漏,同时也可以对后续的开发工作起到很大的帮助:开发人员必须实现所有的场景、测试人员可以根据用例场景来设计测试用例。

    2.3.4 特殊需求

    特殊需求通常是非功能性需求,它为一个用例所专有,但不适合在用例的事件流文本中进行说明。特殊需求的例子包括法律或法规方面的需求、应用程序标准和所构建系统的质量属性(包括可用性、可靠性、性能或支持性需求等)。此外,其他一些设计约束,如操作系统及环境、兼容性需求等,也可以在此节中记录。

    需要注意的是,这里记录的是专属于该用例的特殊需求;对于一些全局的非功能性需求和设计约束,它们并不是该用例所专有的,应把它们记录在《补充规约》中。

    2.3.5 前置和后置条件

    前置条件是执行用例之前必须存在的系统状态,后置条件是用例一执行完毕后系统可能处于的一组状态。

    2.4 检查用例模型
    用例模型完成之后,可以对用例模型进行检查,看看是否有遗漏或错误之处。主要可以从以下几个方面来进行检查:

    • 功能需求的完备性
      现有的用例模型是否完整地描述了系统功能,这也是我们判断用例建模工作是否结束的标志。如果发现还有系统功能没有被记录在现有的用例模型中,那么我们就需要抽象一些新的用例来记录这些需求,或是将他们归纳在一些现有的用例之中。
    • 模型是否易于理解
      用例模型最大的优点就在于它应该易于被不同的涉众所理解,因而用例建模最主要的指导原则就是它的可理解性。用例的粒度、个数以及模型元素之间的关系复杂程度都应该由该指导原则决定。
    • 是否存在不一致性
      系统的用例模型是由多个系统分析员协同完成的,模型本身也是由多个工件所组成的,所以我们要特别注意不同工件之前是否存在前后矛盾或冲突的地方,避免在模型内部产生不一致性。不一致性会直接影响到需求定义的准确性。
    • 避免二义性语义
      好的需求定义应该是无二义性的,即不同的人对于同一需求的理解应该是一致的。在用例规约的描述中,应该避免定义含义模糊的需求,即无二义性。

    3. 系统需求
    RUP中根据FURPS+模型将系统需求分为以下几类:

    • 功能(Functionality)
    • 可用性(Usability)
    • 可靠性(Reliability)
    • 性能(Performance)
    • 可支持性(Supportability)
    • 设计约束等

    除了第一项功能性需求之外的其他需求都归之为非功能性需求。

    3.1 需求工件集
    用例模型主要用于描述系统的功能性需求,对于其他的非功能性需要用其他文档来记录。RUP中定义了如下的需求工件集合。

    • 用例模型:记录功能性需求
      • 用例图:描述参与者和用例之间的关系
      • 用例规约:描述每一个用例的细节信息
    • 补充规约:记录一些全局性的功能需求、非功能性需求和设计约束等
    • 词汇表:记录一些系统需求相关的术语

    在实际应用中,除了这些工件之外,我们还可以根据实际需求灵活选用其他形式的文档来补充说明需求。并不是所有的系统需求都适保合用用例模型来描述的,如编译器,我们很难用用例方法来表述它所处理的语言的方法规则,在这种情况下,采用传统的BNF范式来表述更加合适一些。在电信软件行业中,很多电信标准都是采用SDL语言来描述的,我们也不必用UML来改写这些标准(UML对SDL存在着这样的兼容性),只需将SDL形式的电信标准作为需求工件之一,在其他工件中对其加以引用就可以了。总之,万万不可拘泥于用例建模的形式,应灵活运用各种方式的长处。

    3.2 补充规约
    补充规约记录那些在用例模型中不易表述的系统需求,主要包括以下内容。

    • 功能性
      功能性需求主要在用例模型中刻画,但是也有部分需求不适合在用例中表述。有些功能性需求是全局性的,适用于所有的用例,如出错处理、I18N支持等,我们不需要在所有的用例中描述这些功能性需求,只需要在补充规约中统一描述就可以了。
    • 可用性
      记录所有可用性相关的需求,如系统的使用者所需要的培训时间、是否应附合一些常见的可用性标准如Windows界面风格等。
    • 可靠性
      定义系统可靠性相关的各种指标,包括:
      • 可用性:指出可用时间百分比(xx.xx%),系统处于使用、维护、降级模式等操作的小时数;
      • 平均故障间隔时间(MTBF):通常表示为小时数,但也可表示为天数、月数或年数;
      • 平均修复时间(MTTR):系统在发生故障后可以暂停运行的时间;
      • 精确度:指出系统输出要求具备的精密度(分辨率)和精确度(按照某一已知的标准);
      • 最高错误或缺陷率:通常表示为bugs/KLOC(每千行代码的错误数目)或bugs/function-point(每个功能点的错误数目)。
    • 性能
      记录系统性能相关的各种指标,包括:
      • 对事务的响应时间(平均、最长);
      • 吞吐量(例如每秒处理的事务数);
      • 容量(例如系统可以容纳的客户或事务数);
      • 降级模式(当系统以某种形式降级时可接受的运行模式);
      • 资源利用情况:内存、磁盘、通信等。
    • 可支持性
      定义所有与系统的可支持性或可维护性相关的需求,其中包括编码标准、命名约定、类库、如何来对系统进行维护操作和相应的维护实用工具等。
    • 设计约束
      设计约束代表已经批准并必须遵循的设计决定,其中包括软件开发流程、开发工具、系统构架、编程语言、第三方构件类库、运行平台和数据库系统等等。

    3.3 词汇表
    词汇表主要用于定义项目特定的术语,它有助于开发人员对项目中所用的术语有统一的理解和使用,它也是后续阶段中进行对象抽象的基础。

    4. 调整用例模型
    在一般的用例图中,我们只表述参与者和用例之间的关系,即它们之间的通讯关联。除此之外,我们还可以描述参与者与参与者之间的泛化(generalization)、用例和用例之间的包含(include)、扩展(extend)和泛化(generalization)关系。我们利用这些关系来调整已有的用例模型,把一些公共的信息抽取出来重用,使得用例模型更易于维护。但是在应用中要小心选用这些关系,一般来说这些关系都会增加用例和关系的个数,从而增加用例模型的复杂度。而且一般都是在用例模型完成之后才对用例模型进行调整,所以在用例建模的初期不必要急于抽象用例之间的关系。

    4.1 参与者之间的关系
    参与者之间可以有泛化(Generalization)关系(或称为"继承"关系)。例如在需求分析中常见的权限控制问题(如下图所示),一般的用户只可以使用一些常规的操作,而管理员除了常规操作之外还需要进行一些系统管理工作,操作员既可以进行常规操作又可以进行一些配置操作。


    在这个例子中我们会发现管理员和操作员都是一种特殊的用户,他们拥有普通用户所拥有的全部权限,此外他们还有自己独有的权限。这里我们可进一步把普通用户和管理员、操作员之间的关系抽象成泛化(Generalization)关系,管理员和操作员可以继承普通用户的全部特性(包括权限),他们又可以有自己独有的特性(如操作、权限等)。这样可以显著减速少用例图中通讯关联的个数,简化用例模型,使之更易于理解。


    4.2 用例之间的关系
    用例描述的是系统外部可见的行为,是系统为某一个或几个参与者提供的一段完整的服务。从原则上来讲,用例之间都是并列的,它们之间并不存在着包含从属关系。但是从保证用例模型的可维护性和一致性角度来看,我们可以在用例之间抽象出包含(include)、扩展(extend)和泛化(generalization)这几种关系。这几种关系都是从现有的用例中抽取出公共的那部分信息,然后通后过不同的方法来重用这部公共信息,以减少模型维护的工作量。

    4.2.1 包含(include)

    包含关系是通过在关联关系上应用<<include>>构造型来表示的,如下图所示。它所表示的语义是指基础用例(Base)会用到被包含用例(Inclusion),具体地讲,就是将被包含用例的事件流插入到基础用例的事件流中。


    包含关系是UML1.3中的表述,在UML1.1中,同等语义的关系被表述为使用(uses),如下图。


    在ATM机中,如果查询、取现、转帐这三个用例都需要打印一个回执给客户,我们就可以把打印回执这一部分内容提取出来,抽象成为一个单独的用例"打印回执",而原有的查询、取现、转帐三个例都会包含这个用例。每当以后要对打印回执部分的需求进行修改时,就只需要改动一个用例,而不用在每一个用例都作相应修改,这样就提高了用例模型的可维护性。


    在基础用例的事件流中,我们只需要引用被包含用例即可。

    查询-基本事件流

    1. 用户插入信用卡

    2. 输入密码

    3. 选择查询

    4. 查看帐号余额

    5. 包含用例"打印回执"

    6. 退出系统,取回信用卡

    在这个例子中,多个用例需要用到同一段行为,我们可以把这段共同的行为单独抽象成为一个用例,然后让其他的用例来包含这一用例。从而避免在多个用例中重复性地描述同一段行为,也可以防止该段行为在多个用例中的描述出现不一致性。当需要修改这段公共的需求时,我们也只需要修改一个用例,避免同时修改多个用例而产生的不一致性和重复性工作。

    有时当某一个用例的事件流过于复杂时,为了简化用例的描述,我们也可以把某一段事件流抽象成为一个被包含的用例。这种情况类似于在过程设计语言中,将程序的某一段算法封装成一个子过程,然后再从主程序中调用这一子过程。

    4.2.2 扩展(extend)

    扩展(extend)关系如下图所示,基础用例(Base)中定义有一至多个已命名的扩展点,扩展关系是指将扩展用例(Extension)的事件流在一定的条件下按照相应的扩展点插入到基础用例(Base)中。对于包含关系而言,子用例中的事件流是一定插入到基础用例中去的,并且插入点只有一个。而扩展关系可以根据一定的条件来决定是否将扩展用例的事件流插入基础用例事件流,并且插入点可以有多个。


    例如对于电话业务,可以在基本通话(Call)业务上扩展出一些增值业务如:呼叫等待(Call Waiting)和呼叫转移(Call Transfer)。我们可以用扩展关系将这些业务的用例模型描述如下。



    在这个例子中,呼叫等待和呼叫转移都是对基本通话用例的扩展,但是这两个用例只有在一定的条件下(如应答方正忙或应答方无应答)才会将被扩展用例的事件流嵌入基本通话用例的扩展点,并重用基本通话用例中的事件流。

    值得注意的是扩展用例的事件流往往可以也可抽象为基础用例的备选流,如上例中的呼叫等待和呼叫转移都可以作为基本通话用例的备选流而存在。但是基本通话用例已经是一个很复杂的用例了,选用扩展关系将增值业务抽象成为单独的用例可以避免基础用例过于复杂,并且把一些可选的操作独立封装在另外的用例中。

    4.2.3 泛化(generalization)

    当多个用例共同拥有一种类似的结构和行为的时候,我们可以将它们的共性抽象成为父用例,其他的用例作为泛化关系中的子用例。在用例的泛化关系中,子用例是父用例的一种特殊形式,子用例继承了父用例所有的结构、行为和关系。在实际应用中很少使用泛化关系,子用例中的特殊行为都可以作为父用例中的备选流存在。


    以下是一个用例泛化关系的例子,执行交易是一种交易抽象,执行房产交易和执行证券交易都是一种特殊的交易形式。

    用例泛化关系中的事件流示例如下:


    4.3 调整用例模型
    用例模型建成之后,我们可以对用例模型进行检视,看是否可以进一步简化用例模型、提高重用程度、增加模型的可维护性。主要可以从以下检查点(checkpoints)入手:

    • 用例之间是否相互独立?如果两个用例总是以同样的顺序被激活,可能需要将它们合并为一个用例。
    • 多个用例之间是否有非常相似的行为或事件流?如果有,可以考虑将它们合并为一个用例。
    • 用例事件流的一部分是否已被构建为另一个用例?如果是,可以让该用例包含(include)另一用例。
    • 是否应该将一个用例的事件流插入另一个用例的事件流中?如果是,利用与另一个用例的扩展关系(extend)来建立此模型。

    5. 管理用例模型复杂度
    一般小型的系统,其用例模型中包含的参与者和用例不会太多,一个用例图就可以容纳所有的参与者,所有的参与者和用例也可以并存于同一个层次结构中。对于较复杂的大中型系统,用例模型中的参与者和用例会大大增加,我们需要一些方法来有效地管理由于规模上升而造成的复杂度。

    5.1 用例包
    包(Package)是UML中最常用的管理模型复杂度的机制,包也是UML中语义最简单的一种模型元素,它就是一种容器,在包中可以容纳其他任意的模型元素(包括其他的包)。在用例模型中,我们可以用构造型(Sterotype)<<use case>>来扩展标准UML包的语义,这种新的包叫作用例包(Use Case Package),用于分类管理用例模型中的模型元素。

    我们可以根据参与者和用例的特性来对它们进行分类,分别置于不同的用例包管理之下。例如对于一个大型的企业管理信息系统,我们可以根据参与者和用例的内容将它们分别归于人力资源、财务、采购、销售、客务服务这些用例包之下。这样我们将整个用例模型划分成为两个层次,在第一层次我们看到的是系统功能总共分为五部分,在第二层次我们可以分别看到每一用例包内部的参与者和用例。


    一个用例模型需要有多少个用例包取决你想怎么样来管理用例模型的复杂度(包括参与者和用例的个数,以及它们之间的相互关系)。UML中的包其实就类似于文件系统中的目录,文件数量少的时候不需要额外的目录,文件数量一多就需要有多个目录来分类管理,同样一组文件不同的人会创建不同的目录结构来进行管理,关键是要保证在目录结构下每一个文件都要易于访问。同样的道理存在于用例建模之中,如何创建用例包以及用例包的个数取决于不同的系统和系统分析员,但要保证整个用例模型易于理解。

    5.2 用例的粒度
    我的系统需要有多少个用例?这是很多人在用例建模时会产生的疑惑。描述同一个系统,不同的人会产生不同的用例模型。例如对于各种系统中常见的"维护用户"用例,它里面包含了添加用户、修改用户信息、删除用户等操作,这些操作在该用例的事件流可以表述成为基本流的子事件流(subflow)。


    维护用户-基本事件流

    该基本流由三个子事件流构成:

    1) 添加用户子事件流

    2) 修改用户 子事件流

    3) 删除用户子事件流

    但是你也可以根据该用例中的具体操作把它抽象成为三个用例,它所表示的系统需求和单个用例的模型是完全一样的。


    应该如何确定用例的粒度呢?在一次技术研讨会上,有人问起Ivar Jacoboson博士,一个系统需要有多少个用例?大师的回答是20个,当然他的意思是最好将用例模型的规模控制在几十个用例左右,这样比较容易来管理用例模型的复杂度。在用例个数大致确定的条件下,我们就很容易来确定用例粒度的大小。对于较复杂的系统,我们需要控制用例模型一级的复杂度,所以可以将复杂度适当地移往每一个用例的内部,也就是让一个用例包含较多的需求信息量。对于比较简单的系统,我们则可以将复杂度适度地曝露在模型一级,也就是我们可以将较复杂的用例分解成为多个用例。

    用例的粒度不但决定了用例模型级的复杂度,而且也决定了每一个用例内部的复杂度。我们应该根据每个系统的具体情况,因时因宜地来把握各个层次的复杂度,在尽可能保证整个用例模型的易理解性前提下决定用例的大小和数目。

    5.3 用例图
    用例图的主要作用是描述参与者和用例之间的关系,简单的系统中只需要有一个用例图就可以把所有的关系都描述清楚。复杂的系统中可以有多个用例图,例如每个用例包都可以有一个独立的用例图来描述该用例包中所有的参与者和用例的关系。

    在一个用例模型中,如果参与者和用例之间存在着多对多的关系,并且他们之间的关系比较复杂,如果在同一个用例图中表述所有的参与者和用例就显得不够清晰,这时我们可创建多个用例图来分别表示各种关系。


    如果想要强调某一个参与者和多个用例的关系,你就可以以该参与者为中心,用一个用例图表述出该参与者和多个用例之间的关系。在这个用例图中,我们强调的是该参与者会使用系统所提供的哪些服务。


    如果想要强调某一个用例和多个参与者之间的关系,你就可以以该用例为中心,用一个用例图表述出该用例和多个参与者之间的关系。在这个用例图中,我们强调的是该用例会涉及到哪些参与者,或者说该用例所表示的系统服务有哪些使用者。


    总之在用例建模过程中,你可以根据自己的需要创建任意多个用例图,用不同的用例来强调参与者和用例之间不同的关系。但是最重要的是要考虑整个用例模型的可理解性,如果可以用一个用例图把意思表述清楚,就不要再用第二个,因为越是简洁的模型越易于理解。

    参考资料


    • 样例代码

    • Jeffrey Friedl, Mastering Regular Expressions, O'Reilly

    • Mendel Cooper, Advanced Bash-scrīpting Guide

    • Michael Jang, Mastering Redhat 9

     

    关于作者
    傅纯一,IBM中国有限公司软件部Rational中国区技术销售经理
  • 测试用例编写规范(转载)

    2007-06-09 14:20:10

    原文

    1           目的:统一测试用例编写的规范,以保证使用最有效的测试用例,保证测试质量。

    2           范围:适用于公司对产品的业务流程、功能测试测试用例的编写。

    3           术语解释

    3.1         测试分析:对重要业务、重要流程进行测试前的分析。

    3.2         业务流程测试用例:关于产品业务、重要流程的测试用例。

    4           业务流程测试用例编写原则

       4.1         系统性

       4.1.1       对于系统业务流程要能够完整说明整个系统的业务需求、系统由几个子系统组成以及它们之间的关系;

       4.1.2       对于模块业务流程要能够说明清楚子系统内部功能、重要功能点以及它们之间的关系;

       4.2         连贯性

       4.2.1       对于系统业务流程来说,各个子系统之间是如何连接在一起,如果需要接口,各个子系统之间是否有正确的接口;如果是依靠页面链接,页面链接是否正确;

       4.2.2       对于模块业务流程来说,同级模块以及上下级模块是如何构成一个子系统,其内部功能接口是否连贯;

    5           测试用例设计的方法

    5.1         等价类划分法

    5.1.1       确定等价类的原则

    5.1.1.1     如果输入条件决定了取值范围,或值的个数,则可以确立一个有效等价类和两个无效等价类。

    5.1.1.2     如果输入条件规定了输入值的集合,或者规定了“必须如何”的条件,此时可确立一个有效等价类和一个无效等价类;

    5.1.1.3     如果输入条件是一个布尔量,则可以确定一个有效等价类和一个无效等价类;

    5.1.1.4     如果规定了输入数据的一组值,而且程序对每个输入值分别进行处理,此时可为每一个输入值确立一个有效等价类,此外,针对这组值确立一个无效等价类,它是所有不允许输入值的集合;

    5.1.1.5     如果规定了输入数据必须遵守的规则,则可以确立一个有效等价类(符合规则)和若干个无效等价类(从不同的角度违反规则)。

    5.1.1.6     如果确知,已划分的等价类中各元素在程序中的处理方式不同,则应将此等价类进一步划分成更小的等价类。

    5.1.2       测试用例的选择原则

    5.1.2.1     为每一个等价类规定一个唯一的编号;

    5.1.2.2     设计一个新的测试用例,使其尽可能多的覆盖尚未被覆盖的有效等价类,重复这一步,直至所有的有效等价类都被覆盖过;

    5.1.2.3     设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步,直至所有的无效等价类都被覆盖为止。

    5.2         边界值分析法

    5.2.1       测试用例的选择原则

    5.2.1.1     如果输入了条件规定了值的范围,则应取刚达到这个范围的边界值,以及刚刚超越这个边界范围的值作为测试输入数据;

    5.2.1.2     如果输入条件规定了值的个数,则用最大个数、最小个数、比最大多1、比最小小1的数作为测试输入数据;

    5.2.1.3     根据规格说明的每个输出条件,使用前面的原则;

    5.2.1.4     如果程序的规格说明给出的输入输出域是有序集合,则应选取集合的每一个元素和最后一个元素作为测试用列;

    5.2.1.5     如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构的边界上的值作为测试用例;

    5.2.1.6     分析规格说明,找出其他可能的边界条件。

     6           测试用例设计的原则

    6.1         全面性

    6.1.1       应尽可能覆盖程序的各种路径

    6.1.2       应考虑存在跨年、跨月的数据

    6.1.3       大量数据并发测试的准备

    6.2         正确性

    6.2.1       输入界面后的数据应与测试文档所记录的数据一致

    6.2.2       预期结果应与测试数据发生的业务吻合

    6.3         符合正常业务惯例

    6.3.1       测试数据应符合用户实际工作业务流程

    6.3.2       兼顾各种业务变化的可能

    6.4         仿真性

       人名、地名、电话号码等应具有模拟功能,符合一般的命名惯例;不允许出现与知名人士、小说中人物名等雷同情况。

     6.5         可操作性


           测试用例中应写清测试的操作步骤,不同的操作步骤相对应的操作结果。

     7           测试用例编写格式细则

         7.1         测试用例内容

         7.1.1       具体实施可以采用EXCEL和图形相结合,可用EXCEL编写测试用例的同时插入图形来加以说明。测试用例设计的内容可由:模块名、功能说明或图形说明、测试用例输入、应输出结果、实际输出结果、结论、BUG编号、BUG级别8部分组成。

         7.1.2       在测试用例设计模版中有“业务流程测试用例设计模版”(包含整体业务流程)和“功能测试用例设计模版”两个模板可按需要选择。

         7.2         测试用例表格格式

         7.2.1       表格内容的字体为宋体;

         7.2.2       表格内容的字型为12号;

     8           测试用例优先级


    测试用例优先级


                   


    A


    测试计划中重要的模块功能和业务流程


    B


    测试计划中比较重要的模块功能和业务流程


    C


    测试计划中次重要的模块功能和业务流程


    D


    测试计划中不重要的模块功能和业务流程


    E


    系统小单元、系统容错功能


        对于AB 级应重点考虑

       

    9           BUG级别

        参考软件测试停止标准中的错误级别.

  • 测试用例输入数据的设计方法和测试用例设计方法不可混淆(转载)

    2007-06-07 10:08:34

    [原创]测试用例输入数据的设计方法和测试用例设计方法不可混淆

    测试用例的设计是测试设计的重要内容,关于测试用例的设计方法,当前不少出版的测试书和发表的测试文章,不少存在着表述错误,主要是把测试用例中的输入数据的设计方法与测试用例的设计方法混为一谈,对测试初学者和测试用例设计人员产生误导。

    这种错误的主要表现举例如下:

    测试用例的设计方法包括:
    (1)等价类划分法
    (2)边界值法
    (3)功能图与判定表法
    (4)错误推测法
    (5)用户场景法
    (6)......

    其实,测试用例中输入数据的设计方法只是测试用例设计方法的一个子集,上面列出的集中方法都是确定黑盒测试用例的输入测试数据的一般方法,而不是测试用例的设计方法。

    除了确定输入数据之外,测试用例的设计还包括如何确定测试用例的设计策略,如何组织设计用例,如何从测试需求等文档创建完整的测试用例。

    对测试执行人员来说,测试用例的表示内容包括以下几个方面:
    (1)测试用例的测试目标
    (2)测试用例的被测功能点描述
    (3)测试用例的测试运行环境
    (4)测试用例的执行方法(包括测试步骤,输入测试数据或测试脚本)
    (5)测试期望的结果
    (6)执行测试的实际结果
    (7)其他辅助说明

    从以上几点,我们可以看到输入测试数据只是设计测试用例的一个步骤,而不是全部。

    测试用例的设计是一项复杂的测试工作,测试用例的设计方法需要考虑测试的目标,被测试软件的特性,测试者人力资源的技术和能力,测试组织形式,测试进度、测试成本等多个方面。

    原文

    我说几句吧:最近一直在写测试用例,一直疑惑自己的测试用例特象GUI测试用例,然后不断的思考,努力把数据测试都写成功能测试,因为整个系统是我一个人写,没有人指导,文档不全,自己也没有完全参加过什么系统的培训,完全靠自己。。。发现的问题是功能测试用例不是完完全全等同于数据测试,且数据测试一直被我名为GUI测试,看了这篇文章之后,发现数据测试也不等同于GUI测试,现在有一个很大的问题,就是级联菜单的功能测试用例的设计,呼呼,有人知道的麻烦告诉我一下。。。问题

     

  • 权限测试用例(转载+搜集)

    2007-06-07 09:20:27

    对于权限测试主要包括以下内容

    1、根据需求等相关文档,查看程序设置权限级别是否正确,即每一级别的用户所能执行的功能是否分配正确
    测试方法:建立不同权限级的用户进入系统,查看菜单、操作命令有效、无效设置是否正确
    我觉得吧,这个最重要
    2、对用户名、密码的有效性进行测试
    测试方法:最有效的方法是采用等价类划分的方法
    其中要考虑:
    (1)是否区分大小写
    (2)是否允许重名
    (3)用户名长度测试:有效长度、无效长度
    (4)信息有效性测试:特殊字符、正常字符、空字符
    (5)口令锁定:即输入口令次数的限制,据说这是对付远程暴力攻击猜测密码的有效技术
       
    以上是在测试时需要考虑的测试点,因为信息的多样性,所以对权限测试前要设计好测试用例,否则直接测试,肯定会有遗漏地方
    还要考虑程序访问数据库的权限设置,注意在源程序中不要有默认的用户名和密码连接数据库
    对于单个的权限进行测试问题不大。主要的问题还是在于权限的分级与交叉权限,如果按照排列组合进行权限的交叉将会是一个无比庞大的测试用例。关键在于权限组合--这个价类如何来划分。
    在进行权限测试的时候,优先执行和模拟用户日常使用的权限,也就是使用最频繁的操作。
    考虑哪些权限在使用过程中风险比较大,模拟进行测试。

  • 如何让你的测试用例结构更清晰条理化(转载)(强烈推荐)

    2007-06-01 10:05:20

    原文 by 上官若冰

     

    一个好得测试用例,需要满足16个字:
       结构清晰
       内容准确
       易于统计
       便于维护

    让我们来分析和了解一下测试用例得结构。测试用例的结构一般依照项目的层次,可以划分出一定目录结构。如何来做到结构清晰呢?

    假设,如果有一个复杂而庞大的项目,需要对其每个模块,每个模块中的的功能点,每个功能点中的细节分别设计用例,你将用什么方法来使你得测试点体现得条理又准确呢?下面我们以学生管理系统这样一个项目来做例子分析。如下是这个项目的框架式功能介绍:



    从该项目功能点上可将系统划分成如下的关系:



    任何一个的项目,不管其难以程度,都需要将它由大到小,由复杂到简单去划分。从上面这个关系我们可以得出,一个项目从上到小可以出现多个层级关系:
    * 最大的层级就是一个项目,如学生信息管理系统这个项目;
    * 其次组成这个项目的各个分支(模块)功能,如登陆,数据库管理,学生信息管理,账户管理,成 绩分析统计管理;
    * 再次,每个分支(模块)功能内部,外部功能点。

    所以,至少有三层关系在其中。那么,如何让我们的测试用例也能很好的体现出这样的层级关系呢,我们引入如下概念:

    WookBook:
    模块,各产品根据各自特性划分成不同的子模块

    WookSheet:
    子模块,在各模块基础上分成不同的细分模块

    Secnario:
    A set of test cases ensure that the business process flows are tested from end to end. They may be independent tests or a series of tests that follow each other, each dependent on the output of the previous one.

    Part:
    同一个细分模块中的层次划分,比如按照每个页面一个Part进行细分

    Case:
    用例,针对一个或者多个Objective的具体测试用例

    Pre Requisite
    包括整个子模块以及每个测试用例的测试前提条件

    Test Procedure
    每个分组中的测试用例执行顺序,当测试用例顺序执行时可省略

    WookBook,WookSheet, Secnario, Part, Case的层级关系如下:


    Figure 1: The diagram of relationship among the Worksheet, Scenario, Part and Case in one project



    Figure 2: The Diagram of relationship among the Worksheet, Scenario, Part and Case in one workbook


    现在,我们一起根据上图的层级关系,来为学生管理系统构建其测试用例的框架。

    1. 首先我们用一个excel类型的WookBook来定义这个项目,如C050505 SIMS TC 01 01. xls





    2. 在一个WookBook中,用多个WookSheet去定义项目中的主要功能点





    3. 对模块中需要根据不同场景处理的功能点,再细分出它的场景模块。如在学生信息管理模块,因为权限的不同,所涉及到的场景和功能也会有各自的特点:





    4. 在一个WookSheet里面,根据流程和子功能划分成不同的part, 例如在学生管理_助教这个sheet里面:



    注:在Part中,如果需要继续划分,可以允许Sub-Part


    5. 在每个Part中,设计唯一的Case。Case是测试用例中的最小单位,不可再分。

    这样,一个层次鲜明,条理分明的测试用例的结构框架就出来了。拥有清晰结构的测试用例,不仅阅读方便,同时也为测试用例的管理,提供有效的目录组织。

    例如:
    1.   登陆
    2.   帐号管理
    3.   学生信息管理
    3.1.   管理员权限
      3.1.1.   新增学生信息
      3.1.2.   修改学生信息
      3.1.3.   复制学生信息
      3.1.4.   删除学生信息
      3.1.5.   查询学生信息
    3.2.   教职权限
    3.3.   助教权限
    4.   成绩分析与统计
    5.   数据库管理

  • RUP测试过程实践(转)

    2007-05-26 11:47:31

    RUP(Rational Unified Process,Ratinaol 统一过程) 是rational公司提出的一套软件开发过程,目前最新的版本是2003。RUP的最大特点就是它提供了一套完整的软件开发过程框架,任何人或组织都可以根据自己的需要来对这个过程进行裁剪,并根据自身需要进行调整后使其成为个性化的过程。读者可以参考网络上流传的《RUP2000中文版》。 ( Rational以及 Rational Unified Process 均系 Rational Software Corporation 在美国和其他国家的商标或注册商标。)

      有句老话说:万事开头难。说的是在做事情的时候,通常都是一开始觉得非常困难,但是只要上了道,入了门,就会越来越容易、越来越顺了。写文章也是如此。不过笔者这里说的开头难并不是不会写文章,而是专指不会写文章的开头部分。过去看到过的很多小说,无论是言情的或者武侠的,都会拿很长的篇幅出来作为“引子”,或者叫“楔子”,用来交待一些同小说相关的信息。而一部小说是否能够吸引读者,这部分内容也起了很大的作用。不过,笔者作为一个技术工作者,写的大多数都是技术文章,一般来说文章的内容、结构都是一早就想明白的,唯独这个开头,实在不知如何写起。不过想想也罢,既然不擅长这个,那就努力把内容写的详细、易懂一些,尽量让读者不会有上当的感觉吧。

      软件测试作为一个独立的职位或者说行业,并不是软件业的新生事物,但的确是随着最近两年一些新的思想注入国内软件行业(比如敏捷开发过程、测试驱动开发等),才得以红火起来的——这一点,从相关书籍的出版和销售就可以看得出来,从事软件测试工作的人也渐渐多了起来。但是也因为是刚刚起步,同时国内大多数软件公司都还处在中小型开发团队甚至作坊式开发的层次,不可能提供太多的测试职位,想找到一些高水平并且富有经验的测试人员更是难上加难。这最终也就导致了国内大多数测试从业者都处在“初级阶段”这样一个结果。

      笔者长期活跃在国内的几个比较知名的测试论坛,发现大家希望讨论的问题主要可以分为两种:一种是对于一些测试工作具体操作方法的提问,一种是对于测试工具使用的提问。总体看来,更多的问题集中在了后者。这似乎已经成为了国内软件业的通病,无论是开发还是测试,总希望可以通过某个工具或者语言来一劳永逸的实现某个理想。如果真的希望工具可以改变一切,那么这种愿望总是会落空的。而前者,大多是因为进入了一些刚刚开始重视软件测试的中小型软件公司,而公司的开发团队中负责软件测试的可能只有一两个对软件测试一知半解的新手,当公司需要开展某些方面的测试工作时,缺少这部分相关经验的朋友变选择了通过网络求助。

      测试工具的应用,的确可以提高工作效率,而对于测试工具方面的提问,本来也是无可厚非的,但是在笔者的实践中,如果希望提高一个团队的工作效率和改善工作效果,关注于过程和方法要远远好于关注工具。测试工具的学习、引入和使用,本身就是一个需要消耗大量资源的过程,而且对于工具的选择和引入工具时机的选择也是非常关键的,如果负责这项工作的并不是一位在软件行业沉浸多年,有着丰富测试经验并熟知开发过程的资深人士,那么开发团队将为此承担巨大的风险。即使成功的引入了测试工具,工具本身可以带来的效率的提高也不是在短期内可以体现的。在这里,笔者希望刚刚或者正在准备组建测试团队的软件公司在考虑测试流程的搭建和测试工具引入时,不要给刚刚进入测试行业的朋友太多压力,因为这两项工作都不仅仅同软件测试本身相关,同时也会涉及到项目管理、开发过程方面的内容。轻易的做出一个决定只会对将来在团队中测试工作的开展带来不利的影响。

      在这篇文章中,笔者不打算对测试工具方面的问题提出任何建议,而将对网络上大家关注的测试过程和测试方法方面给出一些具有实际参考价值的经验。

      测试工作应该什么时候开始? 测试用例是不是一定要写?如果写,应该详细到什么程度才会比较好用又容易维护? 什么样的测试用例才是好的测试用例?测试用例如何覆盖测试需求?测试需求和测试用例的关系是什么?怎么样保证测试需求的整理和测试用例的设计不会浪费太多的人力或其他资源?……

      这些问题可谓是老生常谈了,在论坛上、MSN上,或者邮件中,也经常会有朋友问到笔者一些这方面的问题。相信不管是测试新手,还是从事测试工作有一段时间的朋友,都会在工作中不得不经常的要考虑,这些问题到底有没有一个相对明确的答案呢?

      相信看到这里,已经有些朋友在想:这些问题也不是很难啊,在RUP对测试过程的描述中都已经说的很明白了啊。软件测试工作必须要通过计划测试、设计测试、实现测试、执行测试、评估测试几个阶段来完成。其中计划测试阶段需要制定测试计划、整理测试需求;设计测试阶段要设计测试用例和测试过程,要保证测试用例完全覆盖测试需求;实现测试阶段要根据测试用例实现具体的自动化脚本或者手工的操作步骤;执行测试阶段则通过自动化测试工具或人手工来执行那些自动化或手工脚本;最后的评估阶段则要对软件的质量和测试工作自身的质量做出一个客观的评价。RUP中还有详细的工作指南和文档模板呢!

      对,上面所说的这些都没有错,RUP中对于软件测试过程的描述要比笔者上面这段文字详细也生动得多。但是,我们同时也可以看到,RUP中描述到的,更多的是关注于过程的管理,或者更准确的说,RUP是在为我们提供一个大的方向,是一个稳定的、具有指导作用的框架,而不是一些具体的、涉及到操作细节的方法。这也是为什么很多朋友通读了RUP中关于软件测试的部分,但是一旦实际应用仍然找不准方向的原因。笔者今天希望同大家讨论的,则恰恰是这样一些在实践RUP测试过程时,从实际工作中总结出来的工作方法和经验。 对于测试过程方面的规范和一些基本概念, RUP中已经讲的很详细了,笔者在此也就不再赘述,有需要的读者请参照RUP中的相关部分。本文中所关注的内容包括:

      1.在计划测试时,如何确定测试工作的范围和如何整理测试需求;

      2.设计测试用例时,应该如何把握测试用例的粒度;

      3.如何平衡测试用例的可用性和可维护性;

      4.如何通过逆向的测试数据分析方法来保证测试用例的有效性和减少测试工作中资源的浪费;

      5.一个简单的但有实际意义的例子将展示如何将笔者的方法应用到测试过程中。

      这里要事先声明一下,笔者工作三年来,不管是开发还是测试,工作内容始终是围绕着信息管理系统相关业务展开的,而对于测试工作,也一直局限于在系统测试阶段通过手工方法和简单的利用一些测试工具特性进行黑盒测试。因此,在本文下面描述的内容中,难免受到客观环境和笔者个人经验的影响。也正因为如此, 笔者不保证本文中方法和观点适合于所有组织和个人的软件开发过程 。只是希望能够借此为大家提供一种思路,帮助更多人进行个性化的 RUP测试过程实践,共同提高软件测试行业的水平。 另外,本文中使用到的例子,均为笔者的一些假设,如果侵害到任何第三方组织或个人的利益,实属巧合,请通过 E-Mail通知笔者,笔者将在今后再次发布本文时做出相应的调整。

      如何确定测试工作的范围?

      对于一个存在生命周期的软件产品来说,它的开发和测试往往都不是一次性的,因为随着新的需求的出现,以及对原有版本的改进,新的版本会不断的发布(即使对于一些以客户定制方式运作的项目,在开发过程中以及发布后的维护期内,也会产生众多的内部版本)。随着版本的迭代,我们的测试工作也会一直继续下去。而在每一次迭代时,可能在整个工作阶段的开始就受到一些因素的影响,比如市场需求、既定的发布时间、并发的工作导致的资源紧张等等,使我们不得不考虑对软件质量要求的适度,最终使得我们在每个阶段的测试工作的要求或者说所涉及到的内容有可能是不同的。这种变化,最终将会影响到测试需求的确定。

      那么到底该如何确定每次迭代是测试工作的范围呢? 在笔者的实践中,通常把测试工作范围的确定,等价的认为是软件需求的确定。

      不过现在有一个很实际的问题是这样:软件需求在开发过程中不断发生变化,有时候到了后期还会有新的需求添加进来,还有些需求在交付内部测试版本之后又发现原来的需求本身就存在缺陷,之后再次返工,在软件最终发布之前,怎么可能确定的下来呢。啊,这些都是让我们的开发人员和测试人员极其头痛的事情。到底应该怎样在频繁变更的需求中确定哪些部分是我们在某个阶段要测试的内容呢?或者说通过什么样的方法可以改善我们上面提到的那些问题呢? 一个实际的做法就是实现软件需求的版本化控制 。既然说到了这里,就不免要说些题外话。笔者一直都认为软件需求是开发工作和测试工作在制定计划、开展工作时所共同参照的源头和依据,而我们只有在源头上控制好,才能保证下面工作的平稳开展。如果希望某个阶段工作的进度和内容可以明确的定义下来,就必须要考虑软件需求的版本化控制。这里所提到的“软件需求的版本化控制”,是指在一个软件产品的生命周期中,当要进行一个新版本的迭代时,要尽早的确定这个版本中将要实现的需求,并同上个版本做出比较,哪些内容是新增的,哪些内容是被调整过的。在该阶段工作开始之初的工作会议上,明确的向所有需要了解软件需求的涉众传达这部分信息。而如果在该版本的开发过程中不断的出现需求变更的情况,则应该根据市场策略、已公布的发布时间、客户需求、实现的代价、难易程度以及对现有工作的影响等方面,对需求进行适度划分,严格定义当前版本中需要实现的需求,而其他部分,则作为未来版本的软件需求进行考虑。如果有的朋友认为上面的内容还是太理论化,需要一个更实际的、可操作的方法。那么只能说,对于需求的变更,以及因为需求变更而引起的设计的变更,必须要早发现,早讨论,早决定,早调整。这可能更多的要依靠一个团队中相关负责人员的主动工作来保证,而不是依靠一个明确的方法。注意,这里的一个关键是,对于软件需求,同样需要严格按照版本进行管理,或者说使用“基线”进行管理。

      如何整理测试需求?

      一旦当前阶段测试工作的范围确定下来,我们就可以开始考虑测试需求的整理——也就是明确的定义现阶段要“测什么”。测试需求的确定将为我们制定进度时间表、分配资源以及如何确定某个阶段测试工作是否完成提供一个可供衡量的标准。当然,还有更重要的一点,已被确定的测试需求是我们进行测试用例设计和考虑测试覆盖的依据。整理测试需求的第一步,就是要“测试需求”。测试需求?对,不知道您是否想到,这里的“测试需求”中的“测试”是一个动词,指的是对软件需求本身的检查。

      啊?这不是已经超出了测试工作的范围了吗?测试人员不是应该只关心软件的实现同需求是否相符吗?这样对测试人员要求未免太高了。——这是笔者过去同一些朋友谈到测试人员必须对需求进行检查时听到的一些不同的声音。  在这里,首先要明确一个问题,就是软件测试的工作到底做什么?

      在《软件测试》( Ron Patton〔美〕,中文版由机械工业出版社出版,这本书是测试新手入门的经典教材)一书的第10页,有一个明确而简洁的定义:软件测试员的目标是找到软件缺陷,尽可能早一些,并确保其得以修复。

      瞧!这里说要“尽可能早”的“找到软件缺陷”。那这“尽可能早”要早到什么时候呢?

      不知道大家对《软件工程》这本书还有什么印象。至少在笔者看过的多个不同版本的软件工程方面的书中,对于软件缺陷都会有一段类似的描述:缺陷发现的越早,则修复这个缺陷的代价就越小,在需求、设计、编码、测试、发布等不同的阶段,发现缺陷后修复的代价都会比在前一个阶段修复的代价提高10倍(参见下图)。这样看来,上面问题的答案自然就变成了“秃子头上的虱子”:从需求阶段开始!从“测试需求”开始!

      注意,笔者这里的观点并不是说可以取消团队中的“需求评审会议”,这里并不存在冲突。笔者所希望讲述的,是测试人员应该如何看待软件需求,而并不是把“需求评审会议”所承担的责任揽到自己身上。 ?在论坛上也偶尔看到有的朋友问:如何测试需求呢?每次看到这样的提问,笔者内心就禁不住的一阵激动,因为一直以来,讨论这方面问题的朋友的确少之又少。在笔者的实际工作中,对软件需求的检查包括两个方面的内容。一是对软件需求正确性的检查,也就是要保证需求文档中所描述的内容是真实可靠的。在进行这部分工作时,不要迷信所谓的“都是用户提出的真实的需求”,因为我们必须考虑,提出这些需求的涉众,是否真的可以正确的描述自己的需求?我们的需求人员是否真的可以正确的理解用户的需求?有没有一些被用户认为在业务处理上是理所当然、极其平常的事情,而没有作为需求提出来?有没有一些被用户认为他们过去使用的软件已经提供了相应的功能,所以认为我们也应当提供,而没有提出来的?关于这个问题,也曾经有朋友提过不同的看法,认为这样对测试人员的要求太高了——既要熟悉需求人员的工作,又要熟悉软件所涉及的行业的业务。但笔者还是固执的认为,作为测试人员,还是需要对软件产品所涉及的行业的业务有一个全面的、深入的了解——当然,这不是对一个刚刚入门的测试者的要求,但是如果想称为一个优秀的测试者,是难免要付出这部分努力的。

      二是要保证软件需求的可测试性。对于“可测试性”,笔者的概念是:对于一条软件需求或者一个需要实现的特性,必须存在一个可以明确预知的结果,并且可以通过设计一个可以重复的过程来对这个明确的结果进行验证。说的具体一点,就是要保证所有的需要实现的需求都是可以用某种方法来明确的判断是否符合需求文档中的描述。如果对于某条需求或某个特性,无法通过一个明确的方法来进行验证,或者无法预知它的结果,那么就意味着这条需求的描述存在缺陷,应该请需求人员对需求文档进行修改或补充——我们有理由相信,如果作为测试人员对需求无法产生准确的理解,那么开发人员也同样无法对同一条需求产生准确的理解。对于一条确定的软件需求理解的二义性,是在不规范的开发过程中导致返工的一个主要原因。如果认为有必要,那应该在“需求评审会议”上确认所有涉众对需求的理解是一致的。 当然,对于如何提高软件需求的质量,在网络上或者已经出版的书刊中都已经有了很多更加具体、实用的方法,如果有兴趣,大家也可以找来参考。不过,如果您是一位测试者,那么上面这部分内容对您仍然是非常有用的。相信您只要在工作中进行尝试,慢慢的体会,一定会发现这种方法给您带来的好处。?现在当前的测试工作范围已经确定,相应版本的软件需求也通过了评审,我们就可以在这个已经确定的范围内进行测试需求的整理。我们手头上可以参考的东西,通常会有软件需求规约(以下简称 SRS)和用例(以下简称UC)——当然,也可能是一份包含UC的SRS。通过对SRS和UC的阅读,我们可以从文档对特性和业务流程的描述中获得对软件所涉及的业务的一个基本的认识。比如用户在处理实际业务时都要作些什么,多个业务之间的先后顺序是怎样的,用户在处理业务是对于哪些地方有特别的要求,等等。这部分规则,将成为我们的测试需求中最基本的一部分。

      至于测试需求的表现形式,笔者认为大家都可以根据自己的需要进行设计,而没有必要把思路限制在到底使用表格方式还是使用文本方式,只要把握一个原则就行了:在一条测试需求中,用容易理解的自然语言,明确的描述一项需要测试的内容。对于多项测试内容,应尽可能的剥离开来,保证一条测试需求只包含一项测试内容。

      另外,大家也可能注意到了,在软件开发过程的这个阶段,通常是没有用户界面(以下简称UI)可供参考的——虽然RUP中对于需求阶段的工作描述包括了UI设计的部分,但很多时候在这个阶段还是无法提供一个确定的UI的——也就是说我们这时获得的测试需求,将是完全基于业务的,而并不包括基于UI的那部分规则,是同软件的最终具体实现相独立的。

      随着开发工作的继续,开发部门的架构设计文档和详细设计文档也将陆续提交,这时候,我们可以根据设计文档来对已有的测试需求进行增补。注意,这里我们对于设计文档中提到的内容要有选择的采用,只有同SRS或UC中已经定义的部分相符的内容,才可以用来调整我们的测试需求。而同软件需求不相符的部分,则需要同设计人员和需求人员一起讨论,确定下以哪一方作为基准,决定是否需要调整软件需求,然后对测试需求进行相应的增补或者调整。比如对于一些算法,需要考虑设计文档中定义的,同系统实现相关的那些计算公式,是否同软件需求中描述的算法表达的是否是同一个意思?而对于一些约束或者业务规则,设计文档中描述的是否同需求中的相应部分一致? 呵呵,看完上面这部分内容,恐怕又有一部分朋友晕倒在地了,而没有晕倒的那部分朋友 也要提出异议:啊?!你这不是又包含了对开发人员所作的设计工作的检查吗?!刚刚让我们检查需求,现在又让我们检查设计,真的把我们当成全才了!没办法,为了让软件交到我们手上的时候只包含尽量少的缺陷,大家只能再辛苦一下了。我们的工作不应当仅仅限制在软件交付后尽力找到存在的缺陷,而更应该努力及早发现软件缺陷出现的苗头,尽量预防缺陷的出现。虽然并不是说在所有的团队中都应该由测试人员承担“测试需求”和“测试设计”的工作,但是测试人员对这些工作起到的作用,是其他团队中的其他角色所无法替代的。开发部门完成编码实现工作,提交供内部测试的应用程序时,测试人员手头上应该已经准备好了绝大部分测试用例和测试数据,测试部门将开始执行测试。通常在我们执行测试的过程中,即使我们已经从“通过测试”和“失败测试”两个不同的角度准备了非常充分的测试用例和测试数据,但总是有些缺陷的出现是出乎我们意料的,或者说是已有的测试需求和测试用例未能覆盖的。那么,对于这部分缺陷,也应当添加到测试需求中,并设计相应的测试用例,以便于下次版本迭代时进行参考。 OK,相信说到这里,各位看客也应该可以理解我的观点了:对于一个长期发展的团队或者持续开发的产品,它的所有东西都是要不断积累的、不断迭代的。无论对于软件需求还是测试需求,不仅仅是在一个版本的开发过程中,在不同的阶段进行迭代,在产品的整个生命周期中的不同版本间,也是不断迭代和积累的。

      关于测试用例

      什么时候开始设计测试用例?测试用例该怎么写?什么时候算是完成了测试用例的设计工作??上面几条可以算是网络上测试用例设计方面最热的问题,而且每隔一段时间就会被不同的人重新提起。提问的有刚刚进入测试行业的朋友,也有工作一段时间后重新陷入迷茫的“老手”。这几个问题看似简单,可是要想回答的让大家都感到满意,还真是不容易。这样的高难度,笔者也不敢有太多的奢望,还是把自己的经验写出来,希望对大家有些参考价值吧。 测试用例 是为特定目标开发的测试输入、执行条件和预期结果的集合。这些特定目标可以是:验证一个特定的程序路径或核实是否符合特定需求。——这是 RUP中关于测试用例的定义。而在实际工作中,对于测试用例的设计和选择,是考察一个测试人员工作能力和经验的最好方法。 如果您像笔者前面说的那样,已经在工作中开展了测试需求的整理工作,那么测试用例的设计工作就会变成一件自然而然的事情了。如果您在需求阶段就开始了对测试需求的整理,那么当这部分测试需求整理完后,就可以开始相应测试用例的设计了。而随着开发工作的继续,在测试需求被不断的增补、调整后,也应该添加或修改相应的测试用例,以保证测试用例的有效性。这里笔者要特别强调一点:测试用例的完成并非是一劳永逸的,因为测试用例是来源于测试需求,而测试需求的来源包括了软件需求、系统设计、详细设计,甚至包括了软件发布后,在软件产品生命周期结束前发现的所有软件缺陷。来源的多元化注定了测试需求是非常容易发生变化的。一旦测试需求发生变化,则测试用例必须重新维护。如果您对于软件开发的迭代方法比较熟悉,那么就可以对测试用例的设计采用同样的方法。而最终的结果,是您的团队将逐渐拥有越来越全面细致的测试需求和测试用例库,测试人员越来越多的精力,可以放到对测试过程的考虑和测试用例的选择方面。至少在笔者的实践中,这种方法虽然前期需要相对大量的投入,但随着时间的迁移,在没有使用自动化测试工具的情况下,同样大大提高了每个测试人员单位时间内测试工作的效率。关于设计测试用例的方法,无论是在已经出版的专业测试书籍还是网络上的专业测试论坛中,都已经有了很多非常好的文章来专门讲解,笔者也不打算占用太多篇幅重新引用,大家如果有这方面需要,通过网络都可以很容易的找到这些资料。在这里,笔者只是想简单的评论一下很多初学者对这些方法容易产生的误解。相信对于任何一个测试人员来说,等价类划分法和边界分析法都是最早接触,也是最基本、最容易使用的测试用例设计方法。很多朋友也都知道先使用等价类划分法划分出等价类,然后使用边界分析法确定测试需要的边界值。但是很多朋友也提到,在工作了一段时间后,发现这两中方法所能应用的地方越来越少,难道这两种方法真的只能应用在检查编辑框输入类型和输入长度的时候吗?当然不是。对于一些刚刚接触测试工作的朋友提出的这个问题,笔者认为现在市面上的很多测试书籍都要承担很大的责任。比如很多书中,在讲到测试用例设计时,都不约而同的使用同一个例子—— Windows计算器程序。通常是告诉你对于计算器有一个允许的输入范围(比如允许输入一个大于0小于等于100的整数),然后要求设计相应的包含合法数据和不合法数据的测试用例。当然,仅仅是这样一条简单的描述,我们已经可以设计出很多测试用例和测试数据了,比如对于输入范围的考虑,对于输入数据类型的考虑,对于输入长度的考虑等等。但是在我们的实际工作中,很多时候看到的并不是这样过于简单的软件需求描述,很多时候这些内容都是隐含在一些算法或业务规则中的。 我们现在举个例子来看一下: “双倍余额递减法是在不考虑固定资产残值的情况下,以平均年限法折旧率(不扣残值)的两倍作为折旧率,乘以每期期初固定资产折余价值求得每期折旧额的一种快速折旧的方法。
        年折旧率=2÷预计使用年限×100%
        月折旧率=年折旧率÷12
        月折旧额=期初固定资产账面净值×月折旧率
      为了保证固定资产使用年限终了时账面净值与预计净残值相等,在该固定资产折旧年限到期的前两年,将固定资产净值扣除预计净残值后的余额平均摊销。” 上面的内容是我国现行财务制度中关于固定资产折旧的一种方法——双倍余额递减法——的具体算法,大家可以在任何一本会计书中找到这部分内容以及一些相应的例子,不过我们这里并不关心固定资产的折旧到底是怎么一回事。笔者想知道的是大家在看完上面的描述后是否已经发现,我们在设计测试用例时,对于准备进行折旧的固定资产,至少应该包括预计折旧年限小于两年、等于两年和大于两年三种不同的类型。这也应该算是一个等价类划分法和边界分析法的应用吧。实际上,制约我们更好的使用这些测试方法来进行测试用例设计的,并不是方法的应用范围不够宽广,而是因为如果我们不能对这些同实际业务相关的具体算法或业务规则进行深入的分析,就不可能挖掘出深层的测试需求,那么这两种方法的应用也就很有限了。这也是笔者在前面一再强调加深对软件需求了解的原因。对于网络上也有讨论的另外两种方法——错误推测法和因果图法,则分别因为可靠性较差和操作相对复杂而并没有得到广泛的应用。

      如何划分测试用例的粒度?

      我们是不太可能在一个测试用例包含所有测试需求的,因为众多的功能以及不同的路径组合将使这样一个测试用例像巨无霸一般,完全不具有可操作性。——除非您的软件所包含的功能真的又少又简单,不过如果真的有这么一个软件,恐怕也没有测试和发布的必要了。

      当然,这也并不是要您走向另一个极端,为需求中定义的每个特性或功能都提供一个甚至多个测试用例。这里的关键,是要寻找一个合适的度。 笔者推荐的方法是:关注有效功能。

      有效功能:就是指在被测应用所涉及的实际业务中,当用户在手工状态下进行工作时,整个业务流程中对用户来说,具有实际意义那些功能。这个功能的特征是当我们把这个功能单独从计算机软件还原到用户的原始手工状态时,它的完成可以作为用户实际业务的一个阶段性结束的标志,而不是一旦从这个业务流程中独立出来就失去了意义。而该业务完成后,可以为其他用户或业务提供所需要的信息。

      这里区分“有效功能”的关键有如下两个:

      1. 这个功能是可以还原到用户原始的手工业务流程中去的。我们的计算机和软件,都是为了帮助用户解决手工业务中一些烦琐和低效的问题,而提出的一些忠实于原始工作方法或略有变通的解决方案,并不是要改变用户全部的业务流程。所以,应该从用户实际业务的角度来判断功能是否有效。

      2. 这个功能是否可以标志着用户实际业务的一个阶段性结束?并且这项业务完成之后,被完成的业务实体是否可以交付给其他用户或业务以供完成下面的工作? 为了方便理解,我们可以先看一下下面的例子。拿我们常见的财务软件来说,当添加一张会计凭证时,通常是需要填写会计科目,在使用计算机完成工作时,我们可以利用软件的功能,从很多备选科目中选择一个自己需要的科目,或者通过科目代码来输入科目。这项功能很有可能会作为一个特性要求出现在软件需求规格说明书中,那么这个科目的选择或输入是不是一个有效功能呢?让我们试着用上面规则来衡量一下。首先,这个功能在用户手工业务处理过程中是存在的,不同的是这项功能是在用户填写凭证时,在自己的大脑中完成的——用户会根据需要,在自己记忆的科目中选择合适的填写上去,这项功能节省了用户在记忆大量会计科目时付出的额外劳动。我们可以认为这个功能是为用户原来的工作提供了一种简便的、变通的方法。那么这项功能的完成对于用户来说意味着什么呢?我们从上面的描述中可以看到,用户希望软件提供的是可以添加一张完整的凭证这样的功能,而不仅仅是方便填写会计科目。填写会计科目只是用户在添加凭证时的一个步骤,单独把这个功能提取出来对用户来说没有任何实际意义。对于业务流程下游的用户,需要的也不仅仅只是一个会计科目的信息,而是一张包含了会计科目以及其他会计信息的完整的会计凭证,否则就无法进行下面的工作。这样看来,这个功能并不是一个有效的功能,我们可以把它最为需要测试的特性在测试需求中进行描述,却不应该作为一个单独的测试用例出现在我们的测试用例集中。而我们在测试用例中真正关注的,应该是添加会计凭证这个具有实际意义的功能。另外,我们还需要关注如何将多个相互之间存在依赖关系的功能区分为单个的有效功能。例如,现在有 A、B、C三个功能,其中B功能的开始必须依赖于A功能的完成,而且A功能如果出现不同的完成状态,B功能也会做出不同的反应;C功能对B功能的依赖也是如此。那么这时候,我们是否应当将三个相互依赖的功能包含在一个测试用例中呢?这样的做法也不是不可以,但是我们也可以先判断一下,这三个功能是否都是有效功能(您可以使用前面提到的方法来试着评判一下)?如果A、B、C都是独立的有效功能,那么我们可以从上面的描述中发现,它们之间存在的依赖性,可以理解为是一种状态或者说数据的依赖性。后一个功能关心的,是前一个功能最终提供给它的是什么样的“输入”,而不是前一个功能到底作了些什么。这样看来,我们完全可以将它们分别包含在几个独立的测试用例中,而在每个测试用例的开始,把不同的输入作为不同前置条件来描述。

      测试用例是否应该包含所有的细节?

      这是在网络上听到诉苦最多的又一个热点问题:公司要求按照一个严格的规范来开展测试工作,必须书写所有的测试用例文档,要求文档的书写一定要具体,并在执行测试时要参考测试用例文档来进行。如果测试用例文档写的过于简略,则会被领导斥为偷懒。但是,如果文档中的对操作步骤描述的过于具体,像下面这样:

      01.  在“用户名”项中输入“user”;

      02.  在“密码”项中输入“password”;

      03.  点击“确定”按钮。 这样的描述方式表面看起来可操作性似乎是增强了,任何人拿到这个文档都可以充当测试人员,检查一下这个功能是否存在缺陷。但是我们不要忘记,在开发过程中,变更的存在是必然的。一旦需求、设计或者应用程序中的某些细节发生了变化,比如“用户名”变成了“操作员”,“密码”变成了“口令”,“确定”变成了“登录”,或者输入项所接受的数据类型发生了变化,那么同这部分内容相关的所有的测试用例都需要修改。否则,我们凭什么可以保证这些描述不同的测试用例说的是同一样东西?如果我们只有这么一个测试用例,也许一切都不是问题,但是对于一个业务复杂、功能完整的系统,如果按照这样的方法描述测试用例,最终要么产生大量的测试用例文档,要么产生过长的单个文档。无论是那一种,如果发生了类似于上面说的变化,维护文档都将变成一次地狱式的体验。这也是为什么在网络上频频出现的这个问题,而且每次出现都会受到测试同行们的关注的原因。那么这个问题应该任何解决呢?测试用例是不是把所有的流程写出来就可以了呢?如何在减少测试用例文档中包含过多琐碎的细节的同时保证测试用例的可操作性呢?又有什么方法可以提高我们维护测试用例文档的效率呢?笔者的建议是:关注“测试思想”而不是关注操作步骤。要理解这个问题,首先要弄明白测试用例的作用。就像用例一样,测试用例并不是用来描述具体的实现的,而是着重描述处理问题的思路——对于一项明确的测试内容进行测试的思路。作为测试用例的设计人员,如何理解基本流和备选流?如何深入分析并找到所有需要覆盖的路径和需要检查的特性?我们在测试用例中应该用容易理解的自然语言清晰的来描述我们将要如何进行测试,而不是简单的把在应用程序上如何操作的烦琐的步骤记录下来。把测试用例设计当成填写具体操作步骤的表格是人们对测试用例最大的误解。传统的测试用例文档编写有两种方式。一种是填写操作步骤列表:将在软件上进行的操作步骤一步一步详细的记录下来,包括所有被操作的项目和相应的值。另一种是填写测试矩阵:将被操作项作为矩阵中的一个字段,而矩阵中的一条条记录,则是这些字段的值。网络上对于这两种方式孰优孰劣的争论,将大家错误的引导向了两个极端:要么采用 A,要么采用B。而大家却忽视了一点,对于工作方法的争论,本质上同工具的争论并不是一回事(例如曾经的VC、BCB之挣)。如果不同的方法各有优势,我们完全可以通过变通的方法,把优势的部分组合在一起来使用。 操作步骤列表的优势在于对基本流和备选流进行分析后,它可以清晰的描述您的测试思路。而测试矩阵则更适合于用来存放测试数据,特别是那些需要人工赋予一个确定的值的特性。下面,我们来看一个例子,当然,这个例子同样是杜撰的。?需求名称:用户登录安全验证需求描述:用户登录安全验证是为了保证所有登录到系统中的用户,都是由系统管理员预先在系统中设定的。使用系统中不存在的用户名,或者用户名输入正确,但密码输入错误情况,都无法登录到系统中。当用户使用了不存在的用户名或错误的密码时,系统应分别给出适当的提示。如果用户连续三次无法使用正确的用户名和密码登录到系统,则系统应给出适当的提示,并退出当前程序。如果用户使用正确的用户名和密码登录到系统,则退出界面,转到系统主界面。对于用户登录界面和程序主界面,请参考相应的 UI设计文档。

      测试需求:

      01. 检查能否使用正确的用户名和密码登录到系统;

      02. 检查能否使用错误的用户名或密码登录到系统;

      03. 检查使用错误的用户名和密码登录失败超过三次,是否会自动退出当前程序。

      测试用例:

    序号 操作过程描述
    1 输入用户名。
    2 输入密码。
    3 确认登录。

    序号 用户名 密码 预期结果
    1 正确的用户名 正确的密码 登录到系统并转到系统主界面
    2 正确的用户名 错误的密码 无法登录到系统并提示密码错误
    3 错误的用户名 正确的密码 无法登录到系统并提示用户名错误
    4 错误的用户名 错误的密码 无法登录到系统并退出当前程序
    5 空用户名 …… ……

      这个例子并不是按照 RUP提供的标准模板编写的,它的目的只是要为大家展示,用前面所讲的方法,整理出来的测试需求和设计完成的测试用例到底是个什么样子。所以省略了很多细节,不过大家在实际编写测试用例文档的时候,可以根据自己的需要把相应的内容添加上去。同时,也可以在用户名和密码两个字段中填写准备使用的具体数据。 相信大家已经看到,在我们的例子中,测试需求同测试用例之间并非是一一对应的关系因为一条测试需求未必是明确的对应到一个有效功能的(其实测试需求本身同软件需求和用例之间也未必是一一对应的)。而我们的测试用例所关注的,应该是一个有效功能。不过您不用担心,这种情况并不会增加您管理测试需求和测试用例的成本,现在市面上提供的测试工具中已经有些是专门用来维护软件需求、测试需求同测试用例之间的关系的,并且它们提供的强大的视图功能还可以让您很容易的查看到测试用例对测试需求的覆盖情况。对于例子中的测试用例文档,已经被分成了两个部分,一部分是描述了测试用例执行者所应遵循的操作过程,一部分则是在操作中需要使用到的测试数据。这样做的原因是在我们的实际工作中,尤其是在进行功能测试时,很多时候都是使用雷同的操作过程和不同的测试数据来进行的。而使用上面的方法,可以不用再对原本在多个用例中重复出现的操作过程再次描述,而可以把更多的精力放到测试数据的设计和准备上。这样作带来的副产品,就是提高了测试用例的可维护性。怎么?还有人对笔者的观点持怀疑态度?那好吧,那么我们来尝试着证明一下。我们的邮递员要在 5个城市内奔波,并且每个城市都有些邮件需要投递,他需要找到可以一次走遍5个城市的所有路线。这听起来似乎并不是太复杂,利用我们已有的数学知识,很容易就可以得到答案。但是对于我们要测试的内容,通常都会包含更多复杂的规则。 例如,我们有三个文本框,每个文本框每次都只能输入一个英文大写字母,允许输入的值只包括: A、B、C三个字母,当三个文本框输入不同的值的时候,我们不知道会发生什么,也不知道它们之间是否会互相影响,所以需要您来设计可以覆盖所有输入情况的测试用例来测试它。 瞧,这很简单不是吗?如果我们希望每个测试用例在执行时一旦出现缺陷都可以很快的找到导致缺陷的原因,那么最好的办法就是每个测试用例只包括一个同其他测试用例不同的输入值。那么可供我们输入的值都有哪些呢?嗯,对于每个文本框,都至少有 A、B、C三种已经声明的不同的值,另外,我们还要考虑当文本框为空、输入空格、输入非英文字符以及输入A、B、C之外的英文字符的情况。那么按照上面的方法,我们会有多少测试用例呢?答案是343个。这只是一个很简单的例子,我们工作中遇到的软件的业务规则和特性决不会比这少的,那会有多少个测试用例呢?God knows. 不过至少有一点可以肯定,我们无法在原有业务规则发生变化时高效的、无差错的维护完343个测试用例。 但是如果使用我们前面所描述的方法,对于操作过程的改变,您只需要重新维护一次,而对测试数据的维护,也同样是非常简单的,而且并不会因为连续大量修改时出现的错误导致测试用例本身出现歧意。?在将主要精力从“设计”操作步骤转移到设计测试数据之后,我们还将从这种方法中获得更大的益处——通过“逆向测试数据分析”来提高测试工作的整体效率。这种“逆向测试数据分析”的方法,是假设软件中存在多个互相依赖的功能,而且这些功能中包含在“依赖链”最末端,并且不再被其他功能依赖的功能。在我们准备测试数据时,首先从这个“依赖链”最末端的功能开始,分析这个功能会对不同的输入产生哪些不同的结果。然后将所有的输入进行整理,分清哪些输入是来源于其前一个功能的输出。之后,对该功能的上一个功能进行同样的分析,整理出所有的输入和输出,但是这个功能的输出至少应该包含“依赖链”最末端功能接收到的全部输入。继续按照这样的思路循环向上,直到到达“依赖链”开始端的功能。不知道您在工作中有没有遇到这样的情况:在对那些“依赖链”上的功能进行测试时,一开始并没有严格的规范测试数据的使用,结果前一个功能测试时产生的数据根本无法在下面的工作中被很好的利用起来,反而因为大量无效数据增加了后面功能的测试难度。最终不得不对每一个功能重新准备测试数据。大量的时间,浪费在了这些重复而低效的劳动上。?当然,如果希望可以进一步提高某个阶段测试工作的效率,还可以考虑应用“设计测试过程”的方法。这里所说的测试过程,指的是我们在执行测试时所设定的执行测试用例的先后顺序。之所以这样作,是因为可以充分的利用不同功能之间的耦合性,尽量通过一次操作来检查尽量多的内容,从而降低已完成工作的无效性或低效性,最终提高某个阶段的整体工作效率。不过,这项工作同样要求操作者必须对被测的系统所涉及的所有业务以及这些业务之间的关系都非常熟悉才行。?如果您正被测试工作的低效问题所困扰,那么建议您尝试一下上面的方法,希望会对您的工作有所帮助。

      如何评价测试用例的好坏?

      这部分内容的出现,完全是因为不久前同几个朋友的一次讨论。当时大家都认为对于一个测试用例好坏的评价,无外乎两个标准:是否可以发现尚未发现的软件缺陷?是否可以覆盖全部的测试需求?但是后来发现这两个标准对于一些问题是处理不了的。例如,对于一个质量非常好的软件产品,存在的软件缺陷异乎寻常的少,测试用例设计人员准备了大量的测试用例,已经完全覆盖了测试需求,但是只有很少一部分测试用例在执行时发现了缺陷,而其他用例都顺利通过了。那么是否就可以认为顺利通过的那部分测试用例不好呢?对于这个问题,笔者认为不管是测试用例是否可以发现尚未发现的缺陷,还是测试用例对测试需求的覆盖度,都是用来评估测试设计人员工作能力和经验的标准,而对于如何评价测试用例的优劣,应该还有其他标准。当然,在不同的团队中可能存在不同的标准,但下面两条应该是适合于任何团队的。 1.易用性。对于一个即熟悉测试工作,又熟悉被测应用的测试人员,应当可以花费 很少的时间就可以理解测试用例中表达的测试思路,并可以很快的执行完这个测试用例。 2.易维护性。当开发过程中的某些因素影响了测试需求,测试用例的作者或其他测试设 计人员,应该可以花费很少的时间就完成定位并维护所有相关测试用例的工作。

      一些题外话

      曾经有朋友问:如果测试用例同软件的具体实现互相独立,怎么保证测试用例的可操作性呢?怎么保证任何一个人都可以一拿到测试用例就可以直接上机执行测试呢?笔者的回答是:尽量不要让这种事情发生。首先,笔者一直不赞同那种让“任何人”都可以充当测试人员,按照手中测试用例执行测试的做法。因为对于被测应用的全面了解和熟悉,是作为一个测试人员最基本的要求——在前面的文字中对于软件需求的熟悉也多次被强调。我们无法预知让一个对软件以及软件所涉及的实际业务不够熟悉的人,依靠一份他同样不熟悉的操作步骤列表来执行测试会有什么样的结果。但是有一点可以明确,这就好像让一个没有医学背景的人,仅仅依靠一本七年制本硕连读的《内科学》教材来为患者检查是否患有心脏病,最终结果是患者承担了全部的风险。其次,测试用例的本来目的是为了描述我们的“测试思想”——也就是“怎样测”,而是否可以熟练的操作被测应用,并不是在测试用例这个“对象”中所要考虑和保证的,应当剥离出来,作为独立的部分进行处理。而问题的关键,在于如何保证拿到测试用例的人有足够的能力和经验来执行测试,这完全可以通过公司内部对软件及软件所涉及的实际业务的培训,或者软件的操作手册。总之,还是尽量不要随意的加入“任何人”到测试工作中吧。

    原文

     

  • 测试用例示例(转)

    2007-05-25 11:37:27

    测试用例示例(二)

    一个好的测试用例,应该包含以下信息:

    1 软件或项目的名称

    2 软件或项目的版本(内部版本号)

    3 功能模块名

    4 测试用例的简单描述,即该用例执行的目的或方法

    5 测试用例的参考信息(便于跟踪和参考)

    6 本测试用例与其他测试用例间的依赖关系

    7 本用例的前置条件,即执行本用例必须要满足的条件,如对数据库的访问权限

    8 用例的编号(ID),如可以是 软件名称简写-功能块简写-NO.

    9 步骤号、操作步骤描述、测试数据描述

    10)预期结果(这是最重要的)和实际结果(如果有BUG管理工具,这条可以省略)

    11)开发人员(必须有)和测试人员(可有可无)

    12)测试执行日期

    一个测试用例的范例

    项目/软件

    技术出口合同网络申领系统 (企业端)

    程序版本

    1.0.25

     

     

     

    功能模块名

    Login

    编制人  

    xxx

     

     

     

    用例编号-

    TC-TEP_Login_1

    编制时间  

    2002.10.12

     

     

     

    相关的用例

     

     

     

     

     

    功能特性

    用户身份验证

     

     

     

     

     

    测试目的

    验证是否输入合法的信息,允许合法登陆,阻止非法登陆

     

     

     

     

     

    预置条件

    特殊规程说明

    如数据库访问权限

     

     

     

    参考信息

    需求说明中关于登陆的说明

     

     

     

     

     

    测试数据

    用户名=yiyh 密码=1

    操作步骤

    操作描述

    期望结果

    实际结果

    实际结果

    测试状态

    1

    输入用户名称,按登陆按钮。

    用户名=yiyh,密码为空

    显示警告信息请输入用户名和密码!

     

     

     

    查看(1583) 评论(0) 收藏 分享 管理

  • 测试用例模板和sample(转)

    2007-05-22 08:46:58

    测试用例模板(Test Case Template)

    ┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
    ┃用例编号  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃测试优先级 │                           ┃
    ┠──────┼───────────────────────────┨
    ┃用例摘要  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃测试类型  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃用例类型  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃用例设计者 │                           ┃
    ┠──────┼───────────────────────────┨
    ┃设计日期  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃对应需求编号│                           ┃
    ┠──────┼───────────────────────────┨
    ┃对应UI   │                           ┃
    ┠──────┼───────────────────────────┨
    ┃对应UC   │                           ┃
    ┠──────┼───────────────────────────┨
    ┃版本号   │                           ┃
    ┠──────┼───────────────────────────┨
    ┃对应开发人员│                           ┃
    ┠──────┼───────────────────────────┨
    ┃前置条件  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃测试方法  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃输入数据  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃执行步骤  │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┃      │                           ┃
    ┠──────┼───────────────────────────┨
    ┃预期输出  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃实际结果  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃测试日期  │                           ┃
    ┠──────┼───────────────────────────┨
    ┃结论    │                           ┃
    ┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
     
    sample

    ┏━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
    ┃用例编号  │BOSS_ FS_ MARKETING_NEW_01P              ┃
    ┠──────┼───────────────────────────┨
    ┃测试优先级 │高(还有“较高、中、较低、低”几个等级)       ┃
    ┠──────┼───────────────────────────┨
    ┃用例摘要  │新增营销记录                     ┃
    ┠──────┼───────────────────────────┨
    ┃测试类型  │功能性测试(对应还有“安全性测试”等)        ┃
    ┠──────┼───────────────────────────┨
    ┃用例类型  │基本事件(对应还有“备选事件”、“异常事件”)    ┃
    ┠──────┼───────────────────────────┨
    ┃用例设计者 │songfun                        ┃
    ┠──────┼───────────────────────────┨
    ┃设计日期  │2005-04-25                      ┃
    ┠──────┼───────────────────────────┨
    ┃对应需求编号│REQ_ MARKETING_NEW_01                 ┃
    ┠──────┼───────────────────────────┨
    ┃对应UI   │Marketing.htm                     ┃
    ┠──────┼───────────────────────────┨
    ┃对应UC   │UC_ MARKETING_NEW_01                 ┃
    ┠──────┼───────────────────────────┨
    ┃版本号   │Build v0.1                      ┃
    ┠──────┼───────────────────────────┨
    ┃对应开发人员│Frank                         ┃
    ┠──────┼───────────────────────────┨
    ┃前置条件  │操作员登录营销管理系统                ┃
    ┠──────┼───────────────────────────┨
    ┃测试方法  │等价类划分(对应还有“错误猜测法”、“边界值分析”等)┃
    ┠──────┼───────────────────────────┨
    ┃输入数据  │用户名:51testing 性别:男 金额:10元 描述:aaaaaaa  ┃
    ┠──────┼───────────────────────────┨
    ┃执行步骤  │①.进入【营销下发】页面;               ┃
    ┃      │②.点击『增加』按钮;                 ┃
    ┃      │③.输入相应数据;                   ┃
    ┃      │④.点击『确定』按钮;                 ┃
    ┃      │⑤.在后台数据库(test/test@testDB)输入查询语句验证:  ┃
    ┃      │  select * from MarketingTab where ID='1001'    ┃
    ┃      │                           ┃
    ┠──────┼───────────────────────────┨
    ┃预期输出  │㈠.执行步骤④后,页面弹出添加成功提示信息框;     ┃
    ┃      │㈡.执行步骤⑤后查询数据库,记录确实添加成功且数据无误 ┃
    ┃      │                           ┃
    ┠──────┼───────────────────────────┨
    ┃实际结果  │符合预期                       ┃
    ┠──────┼───────────────────────────┨
    ┃测试日期  │2005-05-01                      ┃
    ┠──────┼───────────────────────────┨
    ┃结论    │                           ┃
    ┗━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━┛