什么是好的测试用例[51Testing]

这项研究部分基于NSF制定的EIA-0113539 ITR/SY+PE:“提高软件测试者的教育。” 材料中表达的任何观点、发现和结论或者评论都属于作者,不代表国家科学基金会(NSF)的观点。


摘要

     设计好的测试用例是一门复杂的艺术。其复杂性有三个原因:

       1.测试用例能帮我们发现信息。不同类型的测试对不同类型的信息有效。

       2.可以从多方面证明测试用例是“好的”。但没有一个测试用例在任何方面都很优秀。

       3.人们往往会根据某个测试类型来创建测试用例,比如域测试或基于风险的测试。好的域测试与好的基于风险的测试是不同的。


 什么是测试用例?


  让我们从基础开始,什么是测试用例?

  IEEE标准610(1990)对测试用例的定义如下:

  1) 为特定目的开发的一套测试输入、执行条件以及期望结果的集合,例如运用特殊的程序路径或检查应用是否满足某个特定的需求。
  2) (IEEE Std 829-1983)指定输入、预期结果和一组测试项的执行条件的文档。

 根据Ron Patton(2001,p,65)的定义:

    测试用例是进行软件测试时,尝试使用的特殊输入和遵照的流程。

  
Boris Beizer(1995,p.3)把测试定义为:

  一个或多个子测试的执行顺序就像是一个序列,因为一个子测试的结果和/或最终状态是下一个子测试的输入和/或初始状态。“测试”这一词通常涵盖子测试、专用测试和组测试。

  
Bob Binder(1999,p.47)定义的测试用例:

  测试用例阐明了IUT测试前的状态及其环境、测试输入或条件、以及期望的结果。期望的结果指明了IUT会从测试输入中产生什么结果。这些细则包括了IUT产生的信息、异常、返回值、IUT的结果状态及其环境。测试用例也会为其他构成IUT及其环境的对象指定初始和结果条件。

  实践中,很多事物都可以当作测试用例,即使他们完全不符合准备好的测试文档。

  Brian Marick用一个相关的术语来描述没有完全文档化的测试用例,其测试构思(idea)是:

  “测试构思(idea)是对被测事物的一个简要描述。例如,测试平方根功能,其中一个测试构思可以是“测一个小于0的数”。这个测试想法是检验代码是否有错误处理机制。

  我认为,测试用例是对程序提出的质疑,运行测试的目的就是获取信息,比如程序是否会通过测试。

  在明确测试构思(idea)是什么以及怎样把这种思想应用在产品的某些特殊方面(比如,外观)时,需要还是不需要注明流程细节。依照习惯,如果文件记录是测试用例不可或缺的一部分,那么就请用测试构思代替遵循的所有测试用例。

  测试用例定义的一个重要应用就是:测试用例必须具有一定的能力暴露信息。

      1. 按照这种定义,测试用例的变化范围会随着程序趋于稳定。测试初期,在程序的任何方面都会出现问题的时候,试着用最大的有效值填写数据型的输入字段是一个明智有效的测试方式。数周之后,程序经过数次构建通过测试之后,不再需要测试用例对这个字段进行独立测试,因为它已经具有了处理异常的能力。此时,更多相关的测试用例会同时组合10个不同变量组成边界线,或者会根据较长的测试序列或情景设置边界。

       2. 同样,按照这种定义,对测试用例数量报告的度量是没有意义的。几周前一组20个单变量的测试是有用的,而现在它们已经没用或者合为一体时,应该怎么做?假设你创建了一个包含20个测试的组合测试,这个测试的度量报告是20还是21个测试?仅执行一次的测试是怎样的?因为程序设计变化而使测试没有意义时,没有运行设计和实现的测试是怎样的?

  测试用例定义的另一个应用是设计的测试不是用来暴露缺陷的,目的是信息。通常,缺陷中含有信息,但不是绝对的(这要归功于Marick,1997的见解。)。要取得测试结果,我们需要充分地寻找测试提供的信息。

信息目标


  执行测试时,我们能学到或得到什么?这里有几个例子。

  1. 发现缺陷。  这是测试普遍的目的。运行测试的目的是触发故障并暴露缺陷。  通常,我们是在产品所有感兴趣的部分寻找缺陷。

  2. 缺陷数量最大化。这与“发现缺陷”的区别就是缺陷总数比其覆盖面更重要。即使这是及时发现更多缺陷的有用方法,我们也只是狭隘的关注少数几个高风险的方面。

  3. 阻止不合格产品的发布。测试人员发现产品有严重缺陷时阻止其出库,直到这些问题得到解决。在每次的发布决议会上,测试人员的目的是发现新的瑕疵、缺陷。

  4. 协助管理者做出库的决定。管理者普遍都关心这方面的风险。他们想知道缺陷覆盖面(可能不是过于简单的代码覆盖面统计,而是说明产品发现了多少缺陷,有多少还没有解决),和已发现问题的重要性。书面上出现的重大而不会引起客户不满的问题,可能不会影响产品的出库决定。

  5. 技术支持成本最小化。与技术支持或服务组一起工作,测试组要识别出需要支持的问题。这些通常是与产品相关的外围支撑,是不需要测试的,例如,测试产品需要与特定的打印机一起工作或者从第三方数据库成功地导入数据,可以高频率的访问和数据崩溃。

  6. 遵照规格说明书进行评审。规格说明书中提出的要求都是经过审核的。规格说明书中没有列出的程序特性不(当作目标的一部分)进行审核。

  7.遵照规范。如果规范指明了覆盖范围内的某个类型(例如,至少对产品的每个声明做一个测试),那么测试组要创建合适的测试。如果规范为规格说明书或其他文档指明了一个类型,那么测试组可能需要检查这个类型。一般地说,测试组关注规则中覆盖以及没有覆盖的任何事物。

  8.最小化安全诉讼风险。任何会导致事故或伤害的失误都应该在第一时间被关注。导致时间或数据丢失或数据损坏,但不会对实际事物带来伤害或损坏风险的失误可以不考虑。

  9.发现使用产品的安全情景(即使发现存在缺陷,也能工作的方法)。 有时,寻找的是可以持续不断进行一项作业的方法---其他人依照一组指令会可靠地发送期望产生的有益信息。在这种情况下,测试员不是寻找缺陷。他是在进行试验,按照经验推敲和证实进行作业的方法。

  10.质量评估。这是一个棘手的目标,因为质量是多维度的。质量的性质依赖于产品的性质。例如,一个没有娱乐性的摇动物体的电脑游戏是一个令人厌恶的游戏。

  进行质量评估--测量并报告质量水平--你可能需要对这个产品的大多数重要质量标准有一个明确的定义,并且需要有相关的定义测试结果的理论知识。例如,可靠性不只是关于产品缺陷数的。它是(或经常被定义成)关于可靠性相关的故障数,这些故障期望在一段时间内或一段时间的应用中发生。(可靠性相关?比如,测量可靠性时,企业可能不关心错误信息中的拼写失误。)为了进行预测,你需要一个精确的、依经验形成的合理原型,使测试结果具有可靠性。测试包括获取模型需要的数据,包括产品绝对稳定和不稳定的方面做深入的工作。设想一个可靠的模型是基于统计(可能以严重性类型来区分)每N行代码或每K小时测试发现的BUG数的。查找BUG是重要的,消除重复也是重要的,发现并解决问题使缺陷报告更易于理解、更可能纠正是(在评估的范围内)超出范围的。

  11. 检验产品的正确性。测试是不可能做到这一点的。你可以证明产品不正确,或者你可以证明你在特定时间内用特定的测试策略没有找到任何错误。但是,你不可能进行详尽的、无一遗漏的测试,并且产品可能在你没进行测试的条件下宣告失败。(如果你有一个实在的、可靠的模型)你能做的最好的就是评估出现错误的概率。(参照下面关于可靠性的讨论)。

  12. 质量保证。尽管质量保证是共同的目标,但是不能通过测试来保证质量,不能通过获取度量值来保证质量,不能通过设置标准来保证质量。质量保证包括构建一个高质量的产品,以及为此贯穿在整个开发过程中有熟练技能、有时间、有激情、有方向、有创造自由的人。这超出了一个测试团队的工作范围,但在项目经理和相关实施者的范围内。在进行技术研究的过程中测试团队可获得一定的帮助,但这些研究并不不能保证质量。

 给定一个测试对象,好的测试过程能够提供与这个对象直接相关的信息。

测试在于暴露缺陷


把我们关注的焦点局限在测试团队的两个主要目的上:
   除了开发团队以外的人,会考虑寻找有关的(值得报告的)缺陷并修改这些缺陷。

即使在这些目的范围内,也有很多种不同的好的测试方法。例如,我们也许会说一个测试好于另一个测试,如果它:

  1. 更有力(more powerful)。 根据一般的统计学常识,我是这样理解有利的:如果存在缺陷就更可能暴露出来。注意,对一类缺陷来说,测试1比测试2更有力;对另一类缺陷来说,测试1不如测试2有力。

  2. 更可能产生重大(更有根据、更有说服力的结果)。
如果对一个重大问题有影响力的负责人拒绝修改这个问题。(负责人是指对产品有影响的人。对产品有影响力的负责人是指其喜好和观点会导致产品变更的人。)
  
  3. 更可靠。 可靠的测试更可能像是程序员或其他有影响力的负责人进行的真实的(或合理的)操作集合。“边角测试”是程序员惯用的一个例子,它是说测试或缺陷不可靠:“没有什么可以证明它。”如果有些(或所有)对产品有影响力的负责人同意测试用例的真实性,那么它就是可靠性。

  4. 更可能发现客户遇到的典型事件。设计的很多测试都有很高的可信度。让你的人去影响真正的使用用途。

  越经常出现的活动组就越可能被覆盖或完全覆盖。(我说的活动组是指很多特性同时被用,所以我们就追踪哪些特性集合体按照什么次序被使用了,并根据我们的分析反映这些信息。)更详细的请参阅Musa的(1998)关于软件可靠性工程。

  5. 更易于评价。
问题是程序是否通过了测试?为了易于进行评价,测试人员应该有能力快速、不费力地决定程序是否能通过测试。说程序可能是否会测试是不够的。较难进行评价的是,花费的时间越多,错误越可能在不经意间闪过。面对费时的评估,测试人员会用便捷的方法、另辟蹊径来减少程序是否完好的费时的评测。这些便捷的方法往往不完全精确(就是说,他们会发现不了明显的缺陷或把正确的代码标记为错误的。)更有利于解决问题。

  比如,在不能给有关测试条件提供大量信息以重现问题的情况下,大量的自动测试经常会搞毁系统。
他们对解决问题没有好处。越难重复的测试越不利于解决问题。 如果需要解决的问题是由这个测试发现的,那么这个测试就越难执行,下次测试也越难执行正确。

  6. 更有信息量。测试的价值就是我们能从中学到东西。
大多数情况下,你从通过测试的程序中学到的东西比没通过测试的程序中学到的多,但有信息的测试会教你(减少不确定性)判定程序是否能通过测试。

  i. 比如,如果我们已经在多次构建之后运行了一个测试,并且程序每次都可靠地通过测试,那么我们就期望程序能再次通过这项测试。从重复测试中得到的另外的“通过”结果不会有助于我们对程序的认知。

       ii. 等价类的观点是信息价值的另一个例子。任何测试背后都是一个测试集,他们之间有很高的相似度,我们认为其他测试实质上是这个测试的冗余。用口头的行话说,这就是“等价类”或者“等价部分”。如果这些测试充分的相似,那么第二次测试得到的附加信息会少于第一次测试。

       iii. 这个标准很接近于Karl Popper的实验价值理论(参考Popper 1992)。优秀的实验涵盖了风险预测。这个理论预言人们期望的某些事物并不真实,你偏爱的理论是错的或者会令许多人感到惊讶。Popper分析认为优秀的实验(优秀的测试)是近似于科学哲理的主流核心理念。

       v. 或许,在这里真正要考虑的是,你希望从测试中学到的东西与测试设计、运行的机会成本是均衡的。你花费在测试上的时间就是你没有花费在其他测试或活动上的时间。

  7. 更加复杂。一个复杂的测试包含被测软件的很多方面、或变量、或其他属性。

  当程序有很多变更或一次测了很多特性时,不提倡用复杂测试。如果程序存在很多BUG,复杂测试很快就会失败,也就没必要再执行它了。测试组主要依靠复杂测试发现模块化的BUG。模块化的BUG使很多测试都无法通过,阻碍测试组了解测试应该暴露的信息。因此,测试初期,进行简单测试是合理的、有必要的。当程序更稳定时,或(在极限编程或任何开发生命周期)程序有了更稳定的特性时,更复杂的测试就会变得更有意义。

  8. 更可能帮助测试人员或编码人员洞察产品、客户或者外界环境的某些方面。
    有时,我们通过测试了解产品,了解其如何运转以及存在的风险。随后,我们会设计测试来暴露产品的缺陷,但测试刚开始时,我们会对“是什么”和“怎么测”感兴趣。很多这样的测试不会重复进行。然而,在第一次测试环境中,期望(典型地,单元)测试集合警示编码人员实验上出现代码变更的副作用。在这种环境下,设计的测试会标记出性能变更、化整误差的差异、或者其他某些变更,这些都不是缺陷。程序中意想不到的变更会警示编码人员,她的代码或代码变更影响的原型是不完整或错误的,并引导她进行额外的测试或者解决问题。(感谢Ward Oummingham和Brain Marick提供的这个例子。)

测试类型和测试质量


在黑盒测试的领域中,Kanner&Bach(参考Bach 2003b和Kanner,2002发布在www.testingeducation.org上的课程笔记,以及Kanner,Bach & Pettichord, 2002)描述了11种有优势的黑盒测试:

  ●功能测试 (Function Testing)
  ●域测试 (Domain Testing)
  ●基于规范的测试 (Specificaiton-based Testing)
  ●基于风险的测试 (Risk-based Testing)
  ●压力测试 (Stress Testing)
  ●回归测试 (Regression Testing)
  ●用户测试 (User Testing)
  ●场景测试 (Scenario Testing)
  ●基于状态模型的测试 (State-Model Based Testing
  ●大批量的自动测试 (High Volume Automated Testing)
  ●探索性测试 (Exploratory Testing)

我和Bach收集了一些测试“范例”进行反复领会,其中的一两个统治了测试组团队或天才测试者的思想。经过分析,我们发现有趣的是:
    如果我是一个“场景测试者”(主要根据场景测试的应用定义测试的人),我该如何实际地测这个程序?什么能使场景测试一个比一个好?我会遗漏哪类问题,对我来说难于发现或解释什么,什么又特别简单?

  在这里,用一些怎样的测试用例才算是“好的”的思想,来简单的说明一下这些类型。

功能测试


独立测试每个功能、性能、变量。

  很多测试团队都从非常简单的功能测试开始,再转换到另外一个,通常有若干功能之间的互相交叉,直到程序通过主要功能的测试。

  依照这种方法,好的测试应聚焦于单一的功能,并用中庸的值来测它。我们不希望程序败于这样的测试,但还是会败于有根本性错误的算法、失败构建、或对程序其他部分有牵制力的变更。

  这些测试具有很高的可靠性,并易于评价,但明显没有力度。

  有些测试团队会把大多数时间花费在功能测试上。对他们来说,当每一项都独立彻底地测试过时,测试就完成了。依我的经验,有力度的功能测试类似于域测试,有他们的实力。

域测试


 这种测试的本质是取样。我们把集合划分(区分)成子集合(子域),把一个大的可能进行测试的集合分解成一个小组,并从子集合中选出有代表性的一两个的方法。

  在域测试中,我们关注变量、偶尔是变量的初始值。

  为了测一个给定的变量,测试集把所有你能想象到的值(包括无效值)都赋给这个变量。把这个集合划分成子域,在每个子域中至少进行一个典型测试。一般地,你进行了一个最具代表性的测试,也就是说,你用了一个至少像同类其他元素一样可能暴露错误的值。如果这个变量能达到数据边界,那么最具代表性的就是典型的边界值。

  大多数域测试的讨论都是关于输入变量值能否达到数据边界线的讨论。这些用例区间中最具代表性的是典型的边界用例。

  对于数字变量,一个好的域测试用例集合会检测每个边界值,包括最小值、最大值、一个小于最小值的值、以及一个大于最大值的值。
      1. Whittaker(2003)提出了一个在软件分析时关于许多不同类型变量的深入讨论,包括输入变量、输出变量、中间计算结果、文件系统中存储的值、以及发送到设备或其他程序的数据。

      2. Kaner,Falk & Nguyen(1993)提出了关于变量(配置测试打印机类型)不能达到数据边界的测试的详细分析。

  这些测试比不具代表性的或忽略某些子域(比如,人们经常忽视期望产生错误信息的测试用例)的测试更有力。
第一次运行这些测试用例,或重大相关变更之后,这些测试会产生很多有价值的信息,因为边界/极限值错误是普遍发生的。

  有时,会忽视这些测试发现的BUG,尤其是当你同时测试若干变量的极限值时。(这些测试叫做拐角用例。)他们不需要是可靠的,不需要代表用户期望的,因此,他们也没必要是为解决问题而设的。

基于规范的测试


检查相关文档中的关于程序的每一个声明,例如设计规范、需求列表、用户接口描述,发布原型、或者用户手册。

  在企业中,严格按他们的规范进行测试是非常重要的(有激发性的)。比如,如果规范是合同的一部分,遵照规范是非常重要的。同样,产品必须遵照他们的声明,关乎生命的产品必须遵照所有与安全相关的规范。

  规范驱动的测试一般是比较弱的,对特定规范条目进行测试的这类测试是明显不具有代表性的。

  某些基于规范测试的团队仅仅局限于文档中的描述。对他们来说,一个好的测试集合包含了规范中为每一个声明制定的明确的、有关的测试。

  其他团队对规范中的问题有长远见解。他们发现,对说明详尽的产品进行的大多数信息测试经常会出现规范中的不明确点,或者检查说明不详尽的产品。

基于风险的测试

设想程序失败的一个情形,然后设计一个或多个测试来检查这个程序是否真的会在那种情形下失败。

  一个完美的基于风险测试的集合应该基于一个详尽的风险列表,一个每种情形都能使程序失败的列表。

  一个好的基于风险的测试是一个致力于解决特定风险测试的一个典型代表。

  测试中出现重大失误或者对手产品有明显失误,在这种程度上,基于风险的失败会是非常可靠、非常有激发性的。但是,很多基于风险的测试在理论上(实际应用中不可能发生)是被忽略的。(潜在失误)从实际存在的失误中测出来的风险是很有价值的,会使测试更可靠。

  基于风险的测试在于传递高度的信息价值,因为你有理由相信产品中确实存在你要测的问题。我们能从程序能否通过测试中学到很多东西。

压力测试(Stress Testing)

下面是压力测试的几种不同定义:
    1. 一个普通的定义,用一个峰值脉冲活动来冲击程序,看他是否会失败。

    2. IEEE标准610.12-1990是这样定义的:“测试把致使系统故障做为目标来评价一个系统或组件是否超出其指定的需求。” 

    3. 第三种方法使程序陷入故障以观察其错误行为。比如,如果测试使用了过多的输入,那么你不只是测试规定的限额。你不断增加输入的尺度或速度,直到程序最终失败或者你确信进一步的增加不会导致失败。事实上程序最终失败不会明显的令人惊讶或者激动。当你看到失败,询问暴露了什么弱点以及在低于极限环境下时其中哪些弱点会被触发是令人感兴趣的。Jorgensen(2003)提供了一个这种类型的有诱惑力的例子。

  我是以第三种定义工作的。

  这些测试是很有力度的。

  有些人忽视了压力测试使得对顾客的使用不具代表性,因此不可靠、不具目的性。压力测试的另一个问题是失败是没用的,除非测试提供了一个解决问题的信息,或者领导测试的人员对其应用非常熟悉。

  一个好的压力测试推动了你想增加的极限,包括足够的诊断支持使你在看到失败时,非常容易地发现。

  有些测试人员,比如Alberto Savoia(2000),用类似压力测试来暴露失败,这很难察看系统是否同时运行了若干个任务。这些失败通常在系统的理论限制内暴露出来,因此它们是更可靠、更有目的性,但它们不便于解决问题。

回归测试


 设计、开发和保存测试的目的是有规律地重复利用它们,发生变更之后对程序做重复测试。

  有必要注意的一点(考虑回归测试)是这不是测试类型的一个直交列表。你可以给你的回归测试集合输入域测试或者基于规范的测试或者其他任何测试类型。

  所以这些与其他测试之间有什么不同之处?我将用例子来回答这个问题:

  假设一个测试人员创建了一组域测试并保存之以便于复用。这是域测试还是回归测试?

  在创建测试时如果测试人员主要考虑区分变量,并找到最具代表性的一个时,我认为它是主要是域测试。

  如果测试人员主要考虑创建一个复用测试集合的话,我认为它主要是回归测试。

  如果是第一次设计的回归测试,那他们应该是有力的、可靠的等等。但是,在一个测试多次运行并通过之后,程序不可能在下一次测试时失败,除非发生了较大的变更或者部分代码变更直接参与了这个测试。因此,多数情况下,回归测试会产生很小的信息价值。

  一个好的回归测试是为复用设计的。它是完全文档化的和可维护的。(建议:提高GUI级测试的可维护性,参见Graham & Fewster,1999;Kaner,1998; Pettichord,2002,以及www.perrichord.com上的论文)。

  如果变更减少了程序进行回归测试在功能或范围上的错误,那么一个设计的好的回归测试可能会失败。

用户测试

用户测试是由用户进行的,不是由伪装成用户的测试人员进行的,不是由秘书或者执行者伪装成测试人员、用户进行的。用户是使用最终产品的人。

  用户测试是由用户或测试人员或其他人(有时甚至是顾客软件合同中接受测试的律师)设计的。用户测试的集合可能包括边界值测试、压力测试或任何其他类型的测试。

  设计的有的用户测试是只由用户执行他们并报告程序是否通过这些测试的细节。如果你的目的是提供一个周密的、没有任何显示错误事件机会的系统脚本示例,那么这是设计测试的一个好方法。

  如果你的目的是发现用户在系统实际使用中会遇到的问题,那么你的工作就比较难了。

  Beta测试通常是简单的、有效的用户测试,但是实际上它们是非常难管理的,并且不会产生大量的信息。关于beta测试的一些建议,请参见Kaner,Falk & Nguyen(1993).

  一个好的用户测试,在给用户提供足够的结构来报告结果的有效性时,必须给用户的认知活动有足够的余地(在某种程度上有助于读者理解和解决问题)。

  用户测试中发现的故障一般是可靠的和有目的的。少数用户会执行特别有力度的测试。但有些用户会把程序放到复杂的情形下运行。

场景测试(Scenario Testing)


一个场景就是描述了一个假设情况的故事。在测试上,检查程序如何在这种假设环境下复现。

  理想的场景测试是可靠的、有目的的、易于评估的、复杂的。

  实际上,许多场景在这些特性中的至少一个上是微弱无力的,但人们依然称之为场景。这种模式的关键信息是当你设计一个场景测试并试图完成它时,你需要紧记这4个特性。

  场景测试的一个重要变异是粗糙测试。故事中经常会有一个仅由普通用户使用的序列或数据值。然而,他们会出现在用户错误之外,或异常但似是而非的情形下,或敌对的用户行为下。Hans Buwalda(2000a,2000b)称之为“肥皂杀手”以区别于称之为“肥皂剧”的正常情形。类似的情形在安全测试或其他形式的压力测试下是普遍存在的。

  在RUP(Rational Unified Process)中,场景出自使用用例。(Jacobson,Booch, & Rumbaugh,1999)。场景制定了参与者、角色、事务处理、参与者的目标、以及在试图达到目的的过程中发生的事件。场景是使用用例的一个实例化。一个简单的场景追溯了一个独立使用用例的全过程,指定了数据值以及用例中的路径。一个比较复杂的使用用例会首尾串联若干个使用用例,来追踪给定的任务。(参见Bittner & Spence,2003; Cockburn,2000; Collard,1999; Constantine & Lockwood,1999; Wiegers,1999.)警告性注意,请参见Berger(2001).

  无论如何他们是继承来的,好的场景测试在第一次运行时具有很大的力度。

  不同团队多久执行一个给定的场景测试。

  1. 有些团队创建一个类似回归测试的场景测试池。

  2. 其他团队(像我)一次或在很短时间内执行一个场景测试,然后设计另外一个场景而不是坚持使用一个以前用过的。

  通常,测试人员生成的场景能洞察到产品内部。这在测试初期以及稍后的再一次测试中是非常真实的(当产品已经稳定,测试人员试图了解产品进一步的用途时。)

基于状态模型的测试

在基于状态模型的测试中,你把程序可见的行为模拟成一个状态机,并驱动程序在各状态之间转变,检测与预期模型的一致性。关于这种测试方法的深入讨论在www.model-based-testing.org网站上。

  一般而言,用自动测试把软件行为比作模型,所以存在的失误比较容易发现(易于评估)。

  通常,基于模型的测试是可靠的、有激发性的、易于解决的。可是,因为有太多的状态(El-Far 1995),基于模型的测试经常是被简化的,它着眼于操作方式而不是状态之间的转变。有些抽象的操作方式是明显的、可信的,而对一些相关者来说,其它的似乎是过于宽泛的或不固定的,从而简化测试。另外,如果模型过于简化,那么模型暴露出来的错误会难以解决(Houghtaling,2001)。

  讨论Harry Robinson(2001)在创建软件状态模型方面的经验,他报告:在自动测试完成之前,在建模时就会产生很多缺陷。Elisabeth Hendrickson(2002)训练测试人员把状态模型当作探测测试工具来工作--她的模型不是自动测试产生的,其价值是指导测试人员进行分析。

  El-Far,Thompson & Mottay(2001)和El-Far(2001)讨论在构建一个优秀的基于模型的测试集合时需要考虑的事项。比如,权衡考虑其中的详细程度(越详细的模型找到的缺陷越多,但越难于阅读和维护)。

  更进一步的说明,请参考www.model-based-testing.com网站上的论文。

大批量的自动测试

大批量自动测试包括了大量的测试数据,把结果与一个或更多局部oracle数据库进行比较。

  1. 最简单的局部oracle数据库进行反崩溃运转,如果程序崩溃,那肯定存在BUG。详尽描述及经验报告请参见Nyman(1998,2002)。

  2. 如果停止规则是基于测试结果而不是覆盖准则的,那么基于状态模型的测试就是大批量的。关于随机的基于状态测试的概括性概念,请参见Whittaker(1997)。关于基于状态模型的测试是以覆盖规则结束的讨论,请参见Al-Ghafees & Whittaker(2002)。

  3. Jorgensen(2002)提供了大批量测试的另外一个例子。他的测试从一个有效文件的应用开始,然后他用各种方法在各种环境下毁掉它,把这个已经毁掉的文件供给应用程序。应用程序拒绝了大多数毁坏的文件并继续毁掉了一部分。有时,有的应用程序在处理这些文件时会失去控制。缓冲超限或其它失误允许测试人员来接管应用程序或运行应用程序的机器。如果测试人员能在获取程序前修改数据流,那么任何读取所有类型数据流的程序就都属于这种类型的测试。

  4. Kaner(2000)描述了其他几个大批量自动测试方法的例子。一个常用的方法是:把随机数据应用给要测试的应用程序和另外一个做参考的应用程序进行比较。另一个方法是:运行回归测试,输入一个任意长的随机序列,测试程序能不能一个接一个的通过测试。内存泄露、堆栈毁坏、无用指针或其它无用的东西随着时间不断累积,最后在这个长序列中发生错误。然而另外一个方法是:用长序列动作和用户探查(把测试作为程序的一部分,记录警告和错误信息以响应发生的意外情形。)测试程序以暴露问题。

 大批量测试是一个多变的组合。其本质是这类测试的结构是由人设计的,但每个测试用例的开发、执行、解析是有计算机进行的,它能够为人们的复查标记出可疑错误。几乎完全自动化使运行大量的测试成为可能。

  1. 单个测试通常是微弱的,但他们可以弥补大批量测试的不足。

  2. 因为这种测试不是手工的,所以一些测试暴露的问题不是特别可信或有价值的。一个熟练的测试人员通常把问题设想成在一个比较明显的或重要的环境下出现的问题,然后用一个手工测试来证明它。

  3. 有些大批量测试方法产生的错误很难解决。很容易发现错误发生在一个特定的测试下,但导致错误发生的一个必要条件可能在真正失败之前已经经过了成千上百个测试。解决这些问题的方法就是设计一些比其他测试更有效的测试集合。

探索性测试


探索性测试是“测试人员积极地在设计测试的同时执行这些测试,利用在测试中获取的信息设计新的更好地测试”(Bach 2003a)。

  Bach指出测试是一个跨越纯脚本(测试人员根据脚本明确的开展工作)和纯探索(测试人员的行为不是预先设计好的,并且除了BUG报告他们没有必要产生任何测试文档)的结合体。在这个结合体中任何给定的测试都是投劳的。甚至是由熟练的测试人员执行的杰出的、执行脚本前的测试也是探索性的测试。

  “在原型用例中(Bach称之为“自由的探索性测试”),探索性的测试人员不断地学习他们正在测试的软件、产品的市场、产品会失败的各种情形、产品的弱点(包括在应用历史中哪里出现过问题以及哪些开发这是解决哪种错误的),并用最佳方法测这个软件。在学习这些的同时,探索性的测试人员也在测这个软件,报告他们发现的问题,主张解决他们发现的问题,并根据他们学习过程中获得的信息开发出新的测试。”(Tinkham & Kaner,2003)

  探索性的测试人员可以使用任何类型的测试--域测试、基于规范的测试、压力测试、基于风险的测试、其中任何几个。根本性的问题是不管哪类测试是最好的,只要此时此刻能尽可能地展现测试人员寻找的信息即可。

  探索性测试不是纯粹自发的。

  测试人员需要做深入的研究,比如了解有竞争力的产品、这个或类似产品的失败历史、与程序员和用户交流、阅读规格说明书、与产品一起工作。

  熟练的探索性测试与其他方法以及拙劣的探索性测试的区别是什么,区别是在测试的同时,做探索性测试的人也在忙于工作,学习、计划以及运行测试。好的测试用例提高了测试人员在寻求信息目标方面的知识。探索性测试很高程度上是目标驱动的,但是在测试人员获取到新知识时目标很快机会改变。

结论

生成“好的”测试用例没有简单的公式或规定可以遵循。即使是多年以来在测试方面感兴趣的人也很难做到这一点。

  测试对展现你寻求信息的目的有好处。

  我见过很多测试团队中大多数都坚持使用少数几类测试,以场景测试为主或以域测试为主,等等。只要他们擅长于他们偏爱的测试类型,他们的测试在某些方面就会变得极好。不幸的是,我们希望的测试类型不是在任何方面都极好的测试。为了让测试结果多样化、广泛化,我们必须有各方面的技术。

  致谢这项研究部分是由EIA-0113539 ITR/SY+PE:“提高软件测试者的教育”给予的支持。感谢Andy Thinkham,Al Jorgenson,和Pat McGee给以上的稿件的评论。

原文地址:这里.

英文版本:这里

你可能感兴趣的:(生产力,软件工程)