软件工程课件第9章 软件测试用例设计.docx
《软件工程课件第9章 软件测试用例设计.docx》由会员分享,可在线阅读,更多相关《软件工程课件第9章 软件测试用例设计.docx(95页珍藏版)》请在冰豆网上搜索。
软件工程课件第9章软件测试用例设计
第9章软件测试用例设计
软件质量的好坏很大程度上取决于测试用例的数量和质量。
不论程序员的编程水平、软件设计水平有多高,软件过程执行得如何好,如果没有通过合适数量和质量的测试用例进行测试,其最终的软件质量都是难以保证的。
所以从这个意义上来说,测试用例设计是软件测试的最核心和最重要的内容之一strong。
9.1测试用例设计概述
测试用例是为了特定目的(如考察特定程序路径或验证是否符合特定的需求)而设计的测试数据及与之相关的测试规程的一个特定的集合,或称为有效地发现软件缺陷的最小测试执行单元。
测试用例在测试中具有重要的作用,测试用例拥有特定的书写标准,在设计测试用例时需要考虑一系列的因素,并遵循一些基本的原则。
9.1.1测试用例的重要性
在软件测试过程中需要使用测试用例。
那么,为什么要测试用例?
它们的重要性到底是什么?
下面列出几条叙述,说明在测试过程中使用测试用例的作用。
1.测试用例是测试人员测试过程中的重要参考依据。
不同的测试人员根据相同的测试用例所得到的输出应该是一致的,对于准确的测试用例的计划、执行和跟踪是测试的有效性的有力证明。
2.良好的测试用例具有复用的功能,使得测试过程事半功倍,设计良好的测试用例将大大节约时间,提高测试效率。
3.即使是很小的项目,也可能会有几千甚至更多的测试用例,测试用例可能在数月甚至几年的测试过程中被创建和使用,正确的测试计划会很好地组织这些测试用例并提供给测试人员或者其他项目的人参考和有效的使用。
4.从测试的管理角度来看,测试用例的通过率是检验程序代码质量的例证。
经常说程序代码的质量不高或者程序代码的质量很好,量化的标准应该是测试用例的通过率和软件缺陷(bug)的数目。
5.测试用例也可以作为检验测试人员进度、工作量以及跟踪/管理测试人员的工作效率的因素,尤其适用于对于新的测试人员考核,从而更加合理做出测试安排和计划。
测试用例不是每个人都可以编写的,它需要撰写者对用户场景、功能规格说明、产品的设计以及程序/模块的结构都有比较透彻的了解。
测试人员一开始只能执行别人写好的测试用例,随着项目的进度以及测试人员的成熟,测试人员很快能自己编写测试用例,并可以提供给别人使用。
从测试驱动开发的思想来看,测试用例设计对开发人员提出了挑战,不懂测试用例设计就不能算懂开发。
试想一个开发人员如果连自己写的程序有哪些地方是要测试的都不确定,那么他写出的程序质量是无法得到保证的。
9.1.2测试用例数和软件规模的关系
一组设计良好的测试用例和程序源代码的规模有一定的比例关系,不过对软件质量要求不同的软件对这个比例的要求不同。
比如航天软件的测试用例数量和程序源代码行数的比例就应该比普通民用软件高出很多。
规模比较大的软件和规模小的软件相比,如果要达到相同的软件质量,规模大的软件的测试用例数量和程序代码行数的比例应该比小规模软件更高些。
从理论上来讲,要达到相同的软件质量,测试用例数量和软件规模应该是如图9.1所示的一种曲线关系,即软件规模越大,测试用例数占的比例越大。
图9.1测试用例数量和软件规模的关系
不仅测试用例的数量与软件规模具有图9.1所示的曲线关系,就是测试用例的设计难度也是和软件规模有很大关系:
软件规模越大,测试用例的设计难度就越大。
对于单元测试来说,软件规模的大小和测试用例的数量基本上是成比例的,但集成测试和系统测试的用例数量和软件规模就不是简单的正比关系了,规模越大的软件,模块间的关系越复杂,组合的情况就越多,测试用例的数量也就越大。
9.1.3测试用例设计说明的书写规范
在编写测试用例过程中,需要参考和规范一些基本的测试用例编写标准,在ANSl/IEEE8291983标准中列出了和测试设计相关的测试用例编写规范和模板。
标准模板中主要元素如下。
标识符:
每个测试用例应该有一个惟一的标识,作为所有和测试用例相关的文档/表格引用的基本元素,这些文档/表格包括软件设计说明、测试日志表、测试报告等。
测试项:
测试用例应该准确地描述被测试项及其特征。
例如,做Windows应用程序的窗口测试,测试对象是整个的应用程序用户界面,这样测试项就应该是应用程序的界面的特性要求,如窗口缩放、界面布局、菜单等。
测试环境要求:
用来表明执行该测试用例需要的测试环境,一般来说,应根据被测模块对测试环境的特殊需求来描述测试用例的测试环境。
输入数据:
用来执行测试用例的输入数据。
这些输入可能包括数据、文件,或者操作(例如鼠标的左键单击,键盘的按键处理等)。
对应输出数据:
表示按照指定的环境和输入标准得到的期望输出结果。
测试用例之间的关联:
用来标识该测试用例与其他的测试(或其他测试用例)之间的依赖关系。
在测试的实际过程中,很多的测试用例并不是单独存在的,它们之间可能有某种依赖关系,例如,用例A需要基于用例B的测试结果正确的基础上才能进行,此时需要在A的测试用例中表明对B的依赖性,从而保证测试用例的严谨性。
综上所述,如果使用一个表格来表征测试用例的话,它应该有表9.1所示的格式。
这样的结构,可以在以后组织和跟踪测试用例时使用。
表9.1测试用例的组成
字段名称
类型
是否必选
注释
标识符
整型
是
惟一标识该测试用例的值
测试项
字符型
是
测试的对象
测试环境要求
字符型
否
可能在整个模块里面使用相同的测试环境需求
输入数据
字符型
是
对应输出数据
字符型
是
测试用例间的关联
字符型
否
并非所有的测试用例之问都需要关联
下面的例子是一个常见的Web登录页面,通过这个例子来阐述从功能规格说明书到具体的测试用例编写的整个过程。
用户登录的功能设计规格说明书(摘选)如下:
图9.2用户登录界面的示例
1.用户登录
1.1满足基本页面布局图示(登录界面图如图9.2所示)。
1.2当用户没有输入用户名和密码时,在页面上使用红色字体来提示。
1.3用户密码使用掩码符号(*)来显示。
2.登录出现错误
当出现错误时,在页面的顶部会出现相应的错误提示。
错误提示的内容见3。
3.错误信息描述
3.1用户名输入为空且想要登录,显示“错误:
请输入用户名”。
3.2密码为空且未出现3.1情形,显示“错误:
请输入密码”。
3.3用户名∕密码不匹配,显示“错误:
输入的用户名或密码不正确”。
设计测试用例时即要考虑正确的输入,又要考虑错误的或者异常的输入,还要分析怎样使得这样的错误或者异常能够发生。
在本例中,要考虑特殊字符,特别是脚本语言敏感的字符的输入。
根据这个原则,测试用例设计得如表9.2所示。
表9.2用户登录功能测试用例
字段名称
描述
标识符
1100
测试项
站点用户登录功能测试
测试环境要求
略
输入数据
(1)输入正确的用户名和密码,单击“登录”按钮
(2)输入错误的用户名和密码,单击“登录”按钮
(3)不输入用户名和密码,单击“登录”按钮
(4)输入正确的用户并不输入密码,单击“登录”按钮
(5)输入带特殊字符(/、:
、”、’、#、%,如bei’jing)的用户名和密码,单击“登录”按钮。
(6)三次输入无效的用户名和密码尝试登录
(7)第一次登录成功后,重新打开浏览器登录,输入上次成功登录的用户名的第一个字符
对应输出数据
(1)数据库中存在的用户将能正确登录
(2)错误的或者无效用户登录失败,并在页面的顶部出现红色字体“错误:
用户名或密码输入错误”
(3)用户名为空时,页面顶部出现红色字体提示:
“请输入用户名”
(4)密码为空且用户名不为空时,页面顶部出现红色字体提示:
“请输入密码”
(5)输入带特殊字符(/、:
、”、’、#、%)的用户名和密码,单击“登录”按钮。
(6)三次无效登录后,第四次尝试登录会出现提示信息“您已经三次尝试登录失败,请重新打开浏览器进行登录”,此后的登录过程将被禁止
(7)所有的密码均以“*”方式输出。
测试用例间的关联
1101(有效密码测试)
9.2软件测试用例设计方法
在系统开发的不同阶段,采用不同的测试方法。
在编码前的各个阶段,由于程序还没有编写,主要采用检查、分析、评审等人工测试的方法;在编码阶段及编码后的阶段,针对程序和相关文档,采用静态测试和动态测试,即机器执行和人工检查相结合的方式进行软件测试。
通常,使用静态测试(或者称为静态分析)时不必执行程序,目的是收集有关程序代码的结构信息而非查错。
而动态测试则需要执行程序,目的是查错而非检查程序代码的结构信息。
关于静态测试,在8.4节有简单介绍。
本节仅涉及动态测试,动态测试方法可分为白盒测试和黑盒测试。
9.2.1黑盒测试方法(Black-boxtesting)
黑盒测试主要是根据产品的外部功能来规划测试,检查程序各个功能是否实现,主要的质量属性是否达到要求,其中有无错误。
所以人们又称黑盒测试为功能测试、数据驱动测试或基于规格说明的测试。
它是一种从用户观点出发的测试。
采用黑盒测试方法就意味着测试要在软件的接口处进行。
也就是说,这种方法是把测试对象看做一个黑盒子,测试人员完全不考虑程序内部的逻辑结构和内部特性,只依据程序的需求规格说明,检查程序的功能是否符合它的功能说明。
黑盒测试方法主要是为了发现以下几类错误:
是否有不正确或遗漏了的功能?
在接口上,输入能否正确地接受?
能否输出正确的结果?
是否有数据结构错误或外部信息(例如数据文件)访问错误?
性能以及需求规格说明所规定的其他质量属性是否能够满足要求?
是否有初始化或终止性错误?
所以,用黑盒测试发现程序中的错误,必须在所有可能的输入条件和输出条件中确定测试数据,来检查程序是否都能产生正确的输出。
现在假设一个程序P有输入量X和Y及输出量Z,参看图9.3。
在字长为32位的计算机上运行。
如果X、Y只取整数,考虑把所有的X、Y值都做为测试数据,按黑盒方法进行穷举测试,力图全面、无遗漏地“挖掘”出程序中的所有错误。
这样做可能采用的测试数据组(Xi,Yi)的最大可能数目为
232×232=264
如果程序P测试一组(Xi,Yi)数据需要1毫秒,而且假定一天工作24小时,一年工作365天,要完成264组测试,需要5亿年。
黑盒测试是一种传统的测试方法,有严格的规定和系统的方式可供参考。
应该说黑盒测试不仅能够找到大多数其它测试方法无法找到的错误,而且一些外购软件、参数化软件包以及某些生成的软件,由于无法得到源程序,也只能采用黑盒测试方法进行检查。
因为黑盒测试的测试数据是根据需求规格说明决定的,但实际上,规格说明本身也不见得完全正确,如在需求规格说明中规定了多余的功能或是遗漏掉了某些功能,这些问题对于黑盒测试来说是查不出来的。
9.2.2白盒测试方法(White-boxtesting)
白盒测试基于产品的内部结构来规划测试,检查程序内部操作是否按规定运行,各部分代码是否被充分覆盖。
白盒测试把测试对象看做一个透明的盒子,它允许测试人员利用程序内部的逻辑结构及有关信息,设计或选择测试用例,对程序所有逻辑路径进行测试。
通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致。
因此白盒测试又称为结构测试或逻辑驱动测试。
软件人员使用白盒测试方法,主要想对程序模块进行如下的检查:
对程序模块的独立的执行路径尽可能多地执行测试;
对所有的逻辑判定,取“真”与取“假”的两种情况尽可能多地执行测试;
在循环的边界和运行界限内执行循环体检查循环的执行状态;
测试内部数据结构的有效性,等等。
但是对一个具有多重选择和循环嵌套的程序,不同的路径数目可能是天文数字。
而且即使精确地实现了白盒测试,也不能断言测试过的程序完全正确。
举例来说,现在给出一个如图9.4所示的小程序的流程图,它对应了一个有100行源代码的Pascal语言程序,其中包括了一个执行达20次的循环。
那么它所包含的不同执行路径数高达520(=1013)条,若要对它进行穷举测试,即要设计测试用例,覆盖所有的路径。
假使有这么一个测试程序,对每一条路径进行测试需要1毫秒,同样假定一天工作24小时,一年工作365天,那么要想把如图9.4所示的小程序的所有路径测试完,则需要3170年。
以上两种情况的分析表明,软件测试有一个致命的缺陷,即测试的不完全、不彻底性。
由于任何程序只能进行少量(相对于穷举的巨大数量的测试数据而言)的有限的测试,在发现错误时能说明程序有问题;但在未发现错误时,不能说明程序中没有错误,不能说明程序中没有问题。
为了提高测试效率,就必须精心设计测试用例,就是要从大量的可用测试用例中精心地挑选少量高效的测试数据,尽可能多地把隐藏的错误揭露出来。
图9.4白盒测试中的穷举测试
9.3白盒测试用例设计方法
9.3.1逻辑覆盖
逻辑覆盖即为逻辑结构的覆盖测试,它是以程序内部的逻辑结构为基础的设计测试用例的技术。
它属白盒测试,要求测试者对程序的逻辑结构有清楚的了解。
由于覆盖率的要求不同,逻辑覆盖又可分为:
语句覆盖、判定覆盖、判定∕条件覆盖、条件组合覆盖及路径覆盖。
在下面介绍这几种逻辑覆盖的概念时,均以图9.5所示的程序段为例。
其中有两个判定,每个判定都包含复合条件的逻辑表达式,并用符号“∧”表示“与”运算,“∨”表示“或”运算。
图9.5给出的程序段有4条不同的路径。
为易于识别,对第一个判定的取假分支、取真分支分别命名为b、c,对第二个判定的取假分支、取真分支分别命名为d、e。
这样所有4条路径可表示为:
P1(a→c→e)、P2(a→b→d)、P3(a→b→e)和P4(a→c→d),或简写为ace、abd、abe及acd。
图9.5测试用例设计的参考例子
1.语句覆盖
所谓语句覆盖就是设计若干个测试用例,运行被测程序,使得每一可执行语句至少执行一次。
这里的“若干个”,意味着使用测试用例越少越好。
在图9.5的例子中,测试用例的设计格式如下:
【输入(A,B,X),对应的输出(A,B,X)】
因为正好所有的可执行语句都在路径P1上,所以选择路径P1设计测试用例,就可以覆盖所有的可执行语句。
TEST1:
【(2,0,4),(2,0,3)】 覆盖路径ace【P1】
注意,假设在图9.5所示程序段中两个判定的逻辑运算有问题,例如,第一个判定中的逻辑运算符“∧”错写成了“∨”,或者第二个判定中的逻辑运算符“∨”错写成了“∧”,利用上面的测试用例,仍可覆盖所有4个可执行语句。
这说明虽然做到了语句覆盖,但可能发现不了判定中逻辑运算中出现的错误。
语句覆盖是最弱的逻辑覆盖准则。
2.判定覆盖
所谓判定覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判定的取真分支和取假分支至少执行一次。
判定覆盖又称为分支覆盖。
对于图9.5所给出的例子,如果选择路径P1和P2,就可得满足要求的测试用例:
序号
测试用例
覆盖路径
TEST1:
【(2,0,4),(2,0,3)】
ace【P1】
TEST2:
【(1,1,1),(1,1,1)】
abd【P2】
如果选择路径L3和L4,还可得另一组可用的测试用例:
序号
测试用例
覆盖路径
TEST1:
【(2,1,1),(2,1,2)】
abe【P3】
TEST2:
【(3,0,3),(3,1,1)】
acd【P4】
所以,测试用例的取法不唯一。
注意有例外情形,例如,若把图9.5例中第二个判定中的条件X>1错写成X<1,那么利用上面两组测试用例,仍能得到同样结果。
这表明,只是判定覆盖,还不能保证一定能查出在判定的条件中存在的错误。
判定覆盖同样满足语句覆盖。
注意,并不是所有的判定条件都是两分支的,判定覆盖准则还可扩展到多分支判定(如Pascal的CASE语句和C的switch语句)。
特别当判定条件是多个条件用“and”和“or”联结起来的复合条件表达式的时候,极易出现漏判情形,导致代码出错。
3.条件覆盖
所谓条件覆盖就是设计若干个测试用例,运行被测程序,使得程序中每个判定的每个条件的可能取值至少取得一次。
在图9.5的例子中,我们事先可对所有条件的取值加以标记。
例如,
对于第一个判定:
条件A>1取真值为T1,取假值为F1
条件B=0取真值为T2,取假值为F2
对于第二个判定:
条件A=2取真值为T3,取假值为F3
条件X>1取真值为T4,取假值为F4
则可选取测试用例如下:
序号
测试用例
通过路径
条件取值
覆盖分支
TEST1:
TEST2:
【(2,0,4),(2,0,3)】
【(1,1,1),(1,1,1)】
ace(P1)
abd(P2)
T1,T2,T3,T4
F1,F2,F3,F4
c,e
b,d
这一组测试用例覆盖了所有判定中所有条件的可能取值,也覆盖了所有判定的取真和取假分支。
但是是否满足条件覆盖就一定满足判定覆盖呢?
答案是不一定,请看下面的一组测试用例:
序号
测试用例
通过路径
条件取值
覆盖分支
TEST1:
【(1,0,3),(1,0,4)】
abe(P3)
F1,T2,F3,T4
b,e
TEST2:
【(2,1,1),(2,1,2)】
abe(P3)
T1,F2,T3,F4
b,e
从结果来看,这一组测试用例覆盖了所有条件的取值,但没有覆盖所有的分支。
为解决这一矛盾,需要对条件和分支兼顾,有必要考虑下面的条件―判定覆盖。
4.条件∕判定覆盖
所谓条件∕判定覆盖就是设计足够的测试用例,使得判定语句中每个条件的所有可能取值至少取得一次,同时每个判定语句本身的所有可能分支也至少经历一次。
在图9.5中的的例子中,若T1、T2、T3、T4及F1、F2、F3、F4的含意如前所述,则下面的一组测试用例便可覆盖图9.5的8个条件取值以及4个判定分支。
序号
测试用例
通过路径
条件取值
覆盖分支
TEST1:
TEST2:
TEST3:
【(2,0,4),(2,0,3)】
【(1,0,3),(1,0,1)】
【(3,1,1),(2,1,2)】
ace(P1)
abd(P3)
abe(P2)
T1,T2,T3,T4
F1,T2,F3,T4
T1,F2,F3,F4
c,e
b,e
b,d
条件∕判定覆盖是一个比判定覆盖和条件覆盖更强的覆盖。
该覆盖准则特别注意到这样一种情况:
在有的编程语言中处理XandY时,当X为false时,XandY的结果即为false,不再执行Y的判断;只有当X为true时,才去判断Y,以确定XandY的值。
同样,在处理XorY时,当X为True时,XorY的结果即为true,不再执行Y的判断;只有当X的值为false时,才去判断Y,确定XorY的值。
其执行过程如图9.6所示。
图9.6复合条件表达式的执行流程
在处理测试条件XandY的场合,如果单个条件Y有错,当X为false时,Y未执行,这个错误就没有得到检测而遗漏掉了。
同样,在处理测试条件XorY场合,如果单个条件Y有错,当X为true时,Y未执行,这个错误也会遗漏掉。
所以必须把这种复合测试条件的判定结构改成单一测试条件的判定的嵌套结构,对所有路径选择可执行的测试用例,才能彻底检测各个条件的取值。
将具有复合测试条件的图9.7(a)改成如图9.7(b)所示的等效的只有单个测试条件的判定结构。
从图中可以看出,图9.7(a)的每个判定在图9.7(b)中对应有3条可执行路径,这样至少需要3个测试用例才能覆盖两个串联起来的判定的所有可执行路径。
(a)复合条件的判定结构(b)基本条件的判定结构
图9.7分解为基本判定的例子
针对图9.7(b)的例子,可以设计如下一组测试用例,以覆盖其所有路径:
序号
测试用例
条件取值
覆盖分支
执行条件
TEST1:
TEST2:
TEST3:
【(2,0,4),(2,0,3)】
【(1,0,3),(1,0,4)】
【(3,1,1),(3,1,1)】
T1,T2,T3,T4
F1,T2,F3,T4
T1,F2,F3,F4
c,e
b,e
b,d
T1,T2,T3
F1,F3,T4
T1,F2,F3,F4
5.条件组合覆盖
所谓条件组合覆盖就是设计足够的测试用例,运行被测程序,使得每个判定的所有可能的条件取值组合至少执行一次。
现在我们仍来考察图9.5所给出的例子,先对各个判定的条件取值组合加以标记:
记①(A>1)∧(B=0)作T1,T2,属第一个判定的取真分支;
②(A>1)∧(B≠0)作T1,F2,属第一个判定的取假分支;
③(A≯1)∧(B=0)作F1,T2,属第一个判定的取假分支;
④(A≯1)∧(B≠0)作F1,F2,属第一个判定的取假分支;
⑤(A=2)∨(x>1)作T3,T4,属第二个判定的取真分支;
⑥(A=2)∨(x≯1)作T3,F4,属第二个判定的取真分支;
⑦(A≠2)∨(x>1)作F3,T4,属第二个判定的取真分支;
⑧(A≠2)∨(x≯1)作F3,F4,属第二个判定的取假分支;
对于每个判定,要求所有可能的条件取值的组合都必须取到。
在图9.5中的每个判定各有2个条件,所以各有4个条件取值的组合。
我们取4个测试用例,就可用以覆盖上面8种条件取值的组合。
必须明确,这里并未要求第一个判定的4个组合与第二个判定的4个组合再进行组合。
要是那样的话,就需要42=16个测试用例了。
序号
测试用例
通过路径
覆盖条件
覆盖组合号
TEST1:
【(2,0,4),(2,0,3)】
ace(P1)
T1,T2,T3,T4
①,⑤
TEST2:
【(2,1,1),(2,1,2)】
abe(P3)
T1,F2,T3,F4
②,⑥
TEST3:
【(1,0,3),(1,0,4)】
abe(P3)
F1,T2,F3,T4
③,⑦
TEST4:
【(1,1,1),(1,1,1)】
abd(P2)
F1,F2,F3,F4
④,⑧
这组测试用例覆盖了所有条件的可能取值的组合,覆盖了所有判定的可取分支,但路径漏掉了P4。
测试还不完全。
能够执行程序中所有路径的测试是最强的覆盖测试了。
6.路径覆盖
路径覆盖就是设计足够的测试用例,执行程序中所有可能的路径。
还是以图9.5为例,可以选择如下的一组测试用例来覆盖该程序的全部4条路径。
序号
测试用例
通过路径
覆盖条件
覆盖分支
TEST1:
【(2,0,4),(2,0,3)】
ace(P1)
T1,T2,T3,T4
c,e
TEST2:
【(1,1,1),