软件测试很重要,对软件质量的好坏的影响非常大。软件测试工程师在平时的工作中有一个明确的工作目标,即按照规约来验证质量,设计测试用例,尽可能地多发现软件缺陷。好的测试用例足以覆盖整个测试范围之内的测试实施,以保证发现产品中的更多问题。如何才能达成这样的目标呢?可以借鉴John D.McGregor David在《面向对象的测试》一书中提到了几个观点:
1)出发点乃是有罪推断
测试开始时,QA需要具有这样一种看法,即被测程序一定是有bug的,而且一定存在严重的bug——被测程序完全违背了规格说明书的要求。这个原则是我们进行测试设计的基础出发点。
2)穷举测试是不可能的;
有人认为软件测试很简单,只需穷举,但是,软件测试也很复杂,一个大小适度的程序,其路径排列的数量就会非常大,测试空间会有无限可能。所以在绝大多数情况下,穷举测试是不可能的。比如,穷举路径测试决不能查出程序违反了设计规范,即程序本身是个错误的程序。穷举路径测试不可能查出程序中因遗漏路径而出错,而且穷举路径测试也可能发现不了一些与数据相关的错误。所以说:“程序测试只能证明错误的存在,但不能证明错误不存在”。
3) 应用Pareto最优原则
在软件测试领域,Pareto原则即二八法则,暗示着测试发现的错误中的80%很可能来源于程序模块中20%。问题在于如何孤立这些有疑点的模块并进行彻底的测试。归根结底是如何分配测试资源,从而使整个测试工作的收益最好,应用前人归结的以下的几种方法,通过设计充分的测试用例来覆盖程序逻辑,从而解决20%的问题,可以减少测试工程师的疑虑。<br>
==== 等价类划分 ====
等价类划分是将测试空间划分成若干个子集,并且满足每个子集中的任一数据对揭露程序中的缺陷都是等价的,这些子集就叫做等价类或者叫等价子集。
等价类可以分为有效等价类和无效等价类两种。有效等价类指的是由对程序的规格说明是有意义的、合理的输入数据所构成的集合。无效等价类指对程序的规格说明是不合理的或无意义的输入数据所构成的集合。 在具体问题中,依据规格说明书,有效等价类肯定会有,可以是一个,也可以是多个。无效等价类也必须至少有一个。
实际中的情况一般要复杂的多,所以将等价类划分为有效和无效两种还远远不够,等价类还需要细分为弱等价类,强等价类和理想等价类。弱等价类考虑某个单一缺陷情况下的等价情况,子集里所有数据在这种缺陷假设下是等价的,并且划分成的几个等价类能够覆盖整个测试空间的单一缺陷。强等价类指是在多个缺陷假设前提下,各个等价类中的可测数据在单个或多个缺陷假设下是等价的,并且划分的各个等价子集中各自取一个测试数据可以覆盖整个测试空间的多个缺陷情况。 理想等价类指严格符合定义的等价类,即划分的各个等价类中,每个等价类都满足每个可测数据对揭示所有可能的缺陷都是等价的,并且划分的各个等价类中各自任意取一个可测数据做为测试数据可以将全部的缺陷都揭示出来。理想等价类在实际情况中是很罕见的,除非只有很少的一两种可能的出错情况,否则很难划分成对揭示所有可能缺陷都等价的子集。故在实际使用时,没有必要去寻找理想等价类,否则徒然浪费时间,一般采用强等价类或弱等价类进行测试就足够了。
等价类的划分可以依据以下几个原则:
1) 如果输入条件规定了取值范围,则可确定一个有效等价类和两个无效等价类;
2) 输入条件是输入值的集合,例如布尔表达式,或是规定了“必须如何”的输入规则,则可确定一个有效等价类和一个无效等价类;
3) 输入条件为一组值,且每个的处理方式不同,则每个值一个有效等价类,且有一个无效等价类。
4) 如果确知,已划分的等价类中各元素在程序中的处理方式是不同的,则应将此等价类进一步划分。
值的注意的是,当一个子集的处理过程与输出完全一致时,基本上可以认为是等价类。但是实际中往往同一个等价类中的不同数据对应的输出结果并不相同,所以这种方法并不能对所有的情况都适用。这样就需要用到等价类的否定判别方法。
最容易判定一个子集是否是等价类的方法就是路径判定法,路径判定法的基本思想是:对于子集中的任一数据,如果执行路径并不完全相同,那么这个子集不是等价类。另外一种方法是概率判定法:如果在一个等价子集中,所有数据的揭示缺陷的概率不相同并有一定差距,那么可以认为不是等价类。我们可以从子集中找一个元素,它揭示错误的概率比其他的大,那么至少可以认为该等价类不是强等价类。比如前面讲的x>10的划分,{0~10}这个集合中,在写成x>=10的情况下,10和子集中其他数据揭示缺陷的概率是不同的,0~9都不能发现缺陷,测试会通过,也就是说揭示出这个缺陷的概率为0,而10则能揭示出这个缺陷,所以它们不能划分到同一个等价类里面。
利用等价类进行测试设计时,可以依据以下几个步骤:
1). 提取等价类:首先分析程序规格说明书,确定测试空间; 然后尽可能多地列出预期缺陷类型, 再根据预期缺陷确定等价关系,列出等价类表,将全部等价类集合描述出后对重复的等价类集合进行合并,确定最终等价类。
2). 设计用例:根据确定的等价类集合设计测试用例,然后设计一个测试用例时应该使其尽可能多地覆盖有效等价类,重复此步从而最终使得所有有效等价类均被覆盖。然后再设计一个测试用例,使其只覆盖一个无效等价类划分,依此类推,最终使得所有无效等价类划分均被覆盖。<br>
理想等价类是很少见的,我们通常设计测试用例最好也只能到强等价类这个程度,那么既然无法在等价类中任意选择一个元素进行测试了事,那么该选择哪些数值进行测试呢?Pareto最优原则告诉我们,应该关注边界值。因此,我们接下来就介绍边界值分析法。
==== 边界值分析 ====
边界值分析是等价类划分的补充,它需要在等价类划分域的基础上细化边界值;<br>
边界值分析正处于等价类划分边界或边界附近的状态; 如果能应用边界值分析则一定可以用等价类划分,反之不成立。大量的测试经验表明,输入域的边界比中间更加容易发生错误。在边界值分析时一般边界值分析会使用在最小值、略高于最小值、正常值、略低于最大值和最大值出输入变量值。 除过上述5种变量取值外,还通过输入略高于最大值和略小于最小值的取值来进行健壮性边界测试。<br>
边界值分析的步骤如下:
1) 划分等价类;
2) 分析所划分等价类的边界;
3) 确定测试的强度;
4) 根据强度,选择采用一般或健壮测试,设计测试用例
除了等价类划分和边界值分析这两种常用的测试设计方法外,常用的测试设计方法还有以下几种。
==== 错误推测 ====
错误推测指人们可以通过经验或直觉推测程序中可能存在的各种错误,从而有针对性地编写检查这些错误的用例。错误推测法的特点是没有确定的步骤,很大程度上是凭经验进行的,它适用于可以通过经验直观判断常见错误的测试。使用错误推测法时,首先列举出程序中所有可能有的错误和容易发生错误的特殊情况然后再根据它们选择测试用例。 测试推测法需要在平时的工作中多总结,多积累。
在平时的测试中,需要在以下几个方面多加注意:
1)若在单元测试中程序模块曾经发现过错误,在后期功能测试中就列出这些可能出现问题的地方。
2)根据前一版本中发现的常见错误来有针对性地为当前版本设计用例。
3)在应用软件中可能出错的环节,如内存泄漏、web程序的session失效问题、javascript字符转义等一些常见普遍的问题;
4)由模块之间的关联所联想到的测试;
5)由修复软件的错误推测会带来的问题。
6)再结合一些客观因素,比如产品先前版本的问题以及回归测试中发现新的问题。还有一些包括语言、操作系统、浏览器的限制可能带来的问题。
==== 因果图和判定表 ====
这种方法是一种利用图解法分析输入的各种组合情况,从而设计测试用例的方法。 等价类划分法和边界值分析方法都是着重考虑输入条件,但没有考虑输入条件的各种组合、输入条件之间的相互制约关系。这样虽然各种输入条件可能出错的情况已经测试到了,但多个输入条件组合起来可能出错的情况却被忽视了,但是,如果在测试时必须考虑输入条件的各种组合,可能的组合数目将是天文数字,因此必须考虑采用一种适合于描述多种条件的组合、相应产生多个动作的形式来进行测试用例的设计,这就需要利用因果图(逻辑模型)。 因果图适合于检查程序输入条件的各种组合情况。<br>因果图中的关系有以下几种:<br>
其中,左结点Ci表示输入状态(或称原因),右结点ei表示输出状态(或称结果)。表示原因,通常置于图的左。Ci和ei均可取值0或1,0表示某状态不出现,1表示某状态出现。输入状态之间的约束有以下几种:
[[Image:CausalityDiagram2.JPG]]<br>
输出状态的约束只有M约束(强制):若结果a是1,则结果b强制为0,如图:<br>
[[Image:CausalityDiagram3.JPG]]<br>
判定表是分析和表达多逻辑条件下执行不同操作的情况的工具,一般由因果图生成。它能够将复杂的问题按照各种可能的情况全部列举出来,简明并避免遗漏。因此,利用判定表能够设计出完整的测试用例集合。判定表的组成分为4个部分: <br>
{| width="200" cellspacing="1" cellpadding="1" border="1" |- | 条件桩<br> | 条件项<br> |- | 动作桩<br> | 动作项<br> |}
1) 条件桩(Condition Stub):列出了问题得所有条件。通常认为列出的条件的次序无关紧要。
2) 动作桩(Action Stub):列出了问题规定可能采取的操作。这些操作的排列顺序没有约束。
3) 条件项(Condition Entry):列出针对它左列条件的取值。在所有可能情况下的真假值。
4) 动作项(Action Entry):列出在条件项的各种取值情况下应该采取的动作。
使用因果图和判定表时,可以依据以下几个步骤:
1) 找出哪些是原因,哪些是结果,并给每个原因和结果赋予一个标识符。原因即输入条件或输入条件的等价类, 结果即输出条件。
2) 找出原因与原因之间,原因与结果之间对应的关系,是互斥还是同时满足。
3) 画出原因的排列组合情况;
4) 根据这些对应关系和组合,画出因果图。
5) 对有些实际不可能出现的对应关系,在因果图上用一些记号表明约束或限制条件。
6) 把因果图转换为判定表。
7) 把判定表的每一列拿出来作为依据,设计测试用例。
因果图的缺陷是如果输入条件特别多,而且每个输入条件的取值也特别多的情况下,采用这种方法就会导致测试空间的急剧扩张,又陷入了穷举测试的泥淖,遇到这种情况,就可以尝试下面的成对组合法。
上述的几个方法各有长短,每种方法都可以提供一组有用的测试用例,这组用例可以发现某种类型的问题但不易发现另外几种程序的错误,所以在实际项目中,用例的设计一般是几种方法的组合。一种参考的设计方法可以通过以下几步来操作:
1).在任何情况下都需使用边界值分析(这个方法应包括对输入和输出的边界值进行分析)。<br> 2).必要的话,再用等价分类法补充一些测试用例。<br> 3).再用错误推测法附加测试用例。<br> 4).检查上述例子的逻辑覆盖程度,如果未能满足某些覆盖标准,则再增加足够的测试用例。<br> 5).如果功能说明中含有输入条件的组合情况,则一开始就可先用因果图(判定表)法。<br>
==== 有限状态机 ====
用于描述系统中的不同状态,也用于表征系统所接收的不同输入信息和表述系统在接收不同输入下从一个状态转移到另外一个状态的规则。复杂逻辑的测试通过验证程序的不同状态及转换来实现。有限状态机(Finite State Machine)包括以下几个部分: 一个有限状态集,一个输入集,一个状态转移规则集,它的基本思想是验证状态及其转换,可以依照以下步骤来进行测试设计: <br> 1) 对策略进行分析; <br> 2) 得出所有逻辑状态; <br> 3) 绘制状态图; <br> 4) 根据状态转换触发条件绘制因果图; <br> 5) 由因果图生成判定表; <br> 6) 由判定表生成测试序列; <br> 7) 参考测试序列设计测试数据。 <br>
==== 场景法 ====
场景法是以事件为对象来进行分析的。与前面的几种方法相比,则更像一种测试用例的组织方法。分事件触发、场景和事件流。事件触发时的情景便形成场景,而同一事件不同的触发顺序和处理结果就形成事件流。场景法的核心思想是站在用户的角度,检测软件的功能,关心的是用户需要做什么,而不是产品能做什么。场景法中有以下几个基本概念:<br> – 事件流;同一事件不同的触发顺序和处理结果形成事件流。<br> – 基本流;经过用例的最简单路径。它一般是正常完成某一功能的一系列操作。<br> – 备选流;是基本流执行过程中,在某一特定情况下产生的流程分支。一个备选流可以从基本流开始,然后重新加入基本流中;也可能起源于另一个备选流,或者终止用例而不再加入某个流。<br> – 场景:每个事件触发时的情景便形成了场景。用例的场景用来描述流经用例的路径,由一系列相关的活动组成的,从用例开始到结束,遍历这条路径上的所有的基本流和备选流。 <br> 以下情况下可以考虑使用场景法: <br> 1)测试的功能包括一系列完整的用户操作和事件触发。 <br> 2) 希望测试用例可以清晰的描述整个事件。 <br> 3) 多用于业务逻辑完整且较为复杂的软件系统的测试。 <br> 4) 对于软件测试初期或不稳定的代码,采用复杂的场景测试不如单独测试功能点来的快捷有效。
使用场景法的基本步骤: <br> 1)根据需求文档,描述出程序的基本流及各项备选流; <br> 2)根据基本流和各项备选流生成不同的场景; <br> 3)在每一个场景中针对不同的事件流生成相应的测试用例; <br> 4)对生成的所有测试用例重新复审,去掉多余的测试用例 <br> 5)测试用例确定后,对每一个测试用例确定测试数据值。
场景法主要应用于业务逻辑的测试,通过对业务逻辑流程的描述,到达对整个流程中所发生的各种可能状况的再现,从而发现其中隐藏的问题或没有实现或不需要实现的功能,从而更好的满足软件应用的需求。 它的优点是它是基于事务的,更加灵活,可以应用于复杂应用,更加实用,而且测试结果易于评估,路径覆盖好,可操作性强; 场景法的缺点:无法保证测试覆盖度、复用困难。
==== 成对组合 ====
成对组合又称两两组合、对对组合,它是将所有因素的水平按照两两组合的原则而产生的,成对组合覆盖的概念是Mandl于1985年在测试Ada编译程序时提出的。在软件测试领域,根据统计,绝大多数bug,都是因为两个因素互相作用引起的。成对组合可以依据以下几个步骤来分析:首先是寻找测试对象因素,然后为每个因素确定其取值,再根据前面的两步来生成测试用例,需要尽量用最少的用例集合覆盖全部的因素值,最后为每个用例确定输出结果。成对组合有一个很方便的工具可以用来直接生成测试用例:Mypict,它能够快速生成成对组合,支持有条件组合的成对组合用例的生成,支持更高阶的组合,不限于成对组合测试用例,也支持组合阶的测试用例生成,具体的资料可以查看相关链接:http://www.pairwise.org/。
测试工程师需要详细了解功能设计,深入理解系统功能结构的设计,同时思考黑盒测试方法,解决所有理解上的疑问,并取得产品、开发以及测试三方对功能理解上的一致;然后在前期测试框架的基础上,运用等价类划分、边界值、错误推测、性能检查等测试方法对功能点进行全面的剖析,将每一个功能点进行详细分析,设计测试数据,形成最终的测试执行依据。主导测试过程,以高效高质的工作完成任务。