全部科目 > 软件评测师 >
2010年下半年 上午试卷 综合知识
第 25 题
知识点 白盒测试   白盒测试方法   代码检查法   静态结构分析法   静态质量度量法   测试方法   动态测试   静态测试   静态测试和动态测试  
关键词 白盒测试   测试方法   动态测试   静态测试   白盒   测试  
章/节 测试技术的分类  
 
 
白盒测试也称结构测试或逻辑驱动测试,典型的白盒测试方法包括静态测试动态测试。其中,静态测试除了静态结构分析法静态质量度量法外,还有(25)。
 
  A.  代码检查法
 
  B.  逻辑覆盖法
 
  C.  基本路径测试法
 
  D.  结构覆盖法
 
 




 
 
相关试题     白盒测试方法 

  第62题    2013年下半年  
对于逻辑表达式((a&&b)||c),需要(62)个测试用例才能完成条件组合覆盖。

  第56题    2009年上半年  
以下控制流图的圈复杂度V(g)为(56)。

  第58题    2015年下半年  
以下关于白盒测试的叙述中,不正确的是(58)。

 
知识点讲解
· 白盒测试
· 白盒测试方法
· 代码检查法
· 静态结构分析法
· 静态质量度量法
· 测试方法
· 动态测试
· 静态测试
· 静态测试和动态测试
 
        白盒测试
        白盒测试也称结构测试或逻辑驱动测试,它是按照程序内部的结构测试程序,通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行,检验程序中的每条通路是否都能按预定要求正确工作。
        这一方法是把测试对象看作一个打开的盒子,测试人员依据程序内部逻辑结构相关信息,设计或选择测试用例,对程序所有逻辑路径进行测试,通过在不同点检查程序的状态,确定实际的状态是否与预期的状态一致。
        采用什么方法对软件进行测试呢?常用的软件测试方法有两大类:静态测试方法和动态测试方法。其中软件的静态测试不要求在计算机上实际执行所测程序,主要以一些人工的模拟技术对软件进行分析和测试;而软件的动态测试是通过输入一组预先按照一定的测试准则构造的实例数据来动态运行程序,而达到发现程序错误的过程。这两种测试方法和相应测试用例的设计方法将在相应章节作详细介绍。
 
        白盒测试方法
               代码检查法
               代码检查包括桌面检查、代码审查和走查等,主要检查代码和设计的一致性,代码对标准的遵循、可读性,代码逻辑表达的正确性,代码结构的合理性等方面;发现违背程序编写标准的问题,程序中不安全、不明确和模糊的部分,找出程序中不可移植部分、违背程序编程风格的问题,包括变量检查、命名和类型审查、程序逻辑审查、程序语法检查和程序结构检查等内容。
                      代码检查方式
                             桌面检查
                             这是一种传统的检查方法,由程序员检查自己编写的程序。程序员在程序通过编译之后,对源程序代码进行分析、检验,并补充相关的文档,目的是发现程序中的错误。
                             由于程序员熟悉自己的程序及其程序设计风格,桌面检查由程序员自己进行可以节省很多的检查时间,但应避免主观片面性。
                             代码审查
                             代码审查是由若干程序员和测试员组成一个审查小组,通过阅读、讨论和争议,对程序进行静态分析的过程。
                             代码审查分两步:第一步,小组负责人提前把设计规格说明书、控制流程图、程序文本及有关要求、规范等分发给小组成员,作为审查的依据。小组成员在充分阅读这些材料后,进入审查的第二步,召开程序审查会。在会上,首先由程序员逐句讲解程序的逻辑。在此过程中,程序员或其他小组成员可以提出问题,展开讨论,审查错误是否存在。实践表明,程序员在讲解过程中能发现许多原来自己没有发现的错误,而讨论和争议则促进了问题的暴露。例如,对某个局部性小问题修改方法的讨论,可能发现与之牵连的其他问题,甚至涉及到模块的功能说明、模块间接口和系统总体结构的大问题,从而导致对需求的重定义、重设计和重验证,进而大大改善了软件质量。
                             在会前,应当给审查小组每个成员准备一份常见错误的清单,把以往所有可能发生的常见错误罗列出来,供与会者对照检查,以提高审查的实效。
                             这个常见错误清单也称为检查表,它把程序中可能发生的各种错误进行分类,对每一类列举出尽可能多的典型错误,然后把它们制成表格,供再审查时使用。
                             走查
                             走查与代码审查基本相同,其过程分为两步。
                             第一步也是把材料先发给走查小组每个成员,让他们认真研究程序,然后再开会。开会的程序与代码审查不同,不是简单地读程序和对照错误检查表进行检查,而是让与会者“充当”计算机,即首先由测试组成员为所测程序准备一批有代表性的测试用例,提交给走查小组。走查小组开会,集体扮演计算机角色,让测试用例沿程序的逻辑运行一遍,随时记录程序的踪迹,供分析和讨论用。
                             人们借助测试用例的媒介作用,对程序的逻辑和功能提出各种疑问,结合问题开展热烈的讨论和争议,能够发现更多的问题。
                             代码检查应在编译和动态测试之前进行,在检查前,应准备好需求描述文档、程序设计文档、程序的源代码清单、代码编码标准和代码缺陷检查表等。
                             在实际使用中,代码检查能快速找到缺陷,发现30%~70%的逻辑设计和编码缺陷,而且代码检查看到的是问题本身而非征兆。但是代码检查非常耗费时间,而且代码检查需要知识和经验的积累。
                             代码检查可以使用测试软件进行自动化测试,以利于提高测试效率,降低劳动强度,或者使用人工进行测试,以充分发挥人力的逻辑思维能力。
                      代码检查项目
                      . 检查变量的交叉引用表:重点是检查未说明的变量和违反了类型规定的变量;还要对照源程序,逐个检查变量的引用、变量的使用序列、临时变量在某条路径上的重写情况,局部变量、全局变量与特权变量的使用。
                      . 检查标号的交叉引用表:验证所有标号的正确性,检查所有标号的命名是否正确,转向指定位置的标号是否正确。
                      . 检查子程序、宏、函数:验证每次调用与所调用位置是否正确,确认每次所调用的子程序、宏、函数是否存在,检验调用序列中调用方式与参数顺序、个数、类型上的一致性。
                      . 等价性检查:检查全部等价变量的类型的一致性,解释所包含的类型差异。
                      . 常量检查:确认常量的取值和数制、数据类型,检查常量每次引用同它的取值、数制和类型的一致性。
                      . 标准检查:用标准检查工具软件或手工检查程序中违反标准的问题。
                      . 风格检查:检查发现程序在设计风格方面的问题。
                      . 比较控制流:比较由程序员设计的控制流图和由实际程序生成的控制流图,寻找和解释每个差异,修改文档并修正错误。
                      . 选择、激活路径:在程序员设计的控制流图上选择路径,再到实际的控制流图上激活这条路径。如果选择的路径在实际控制流图上不能被激活,则源程序可能有错。
                      . 对照程序的规格说明,详细阅读源代码,逐字逐句进行分析和思考,比较实际的代码和期望的代码,从它们的差异中发现程序的问题和错误。
                      . 补充文档:桌面检查的文档是一种过渡性的文档,不是公开的正式文档。通过编写文档,也是对程序的一种下意识的检查和测试,可以帮助程序员发现和抓住更多的错误。管理部门也可以通过审查桌面检查文档,了解模块的质量、完全性、测试方法和程序员的能力。
                      根据检查项目可以编制代码规则、规范和检查表等作为测试用例,如编码规范、代码检查规则、缺陷检查表等。
                      编码规范
                      编码规范是程序编写过程中必须遵循的规则,一般会详细规定代码的语法规则、语法格式等,如下表所示。
                      
                      编码规范
                      
                      
                      
                      代码检查规则
                      在代码检查中,需要依据被测软件的特点,选用适当的标准与规则规范。在使用测试软件进行自动化代码检查时,测试工具一般会内置许多的编码规则,例如,Parasoft公司用于C/C++语言测试的C++Test,内置了5类规则:通用规则、C++编码规则、C编码规则、Meyers-Klaus规则和自定义规则。我们需要根据编程语言以及被测程序的特点,挑选适当的规则进行检查。
                      例如,在测试中,可以选择下表中的规则进行自动化测试,在此基础上使用桌面检查、代码走查、代码审查等人工检查的方法仔细检查程序的结构、逻辑等方面的缺陷。
                      
                      代码检查规则
                      缺陷检查表
                      在进行人工代码检查时,代码缺陷检查表是我们用到的测试用例。
                      代码缺陷检查表中一般包括容易出错的地方和在以往的工作中遇到的典型错误,如下所示。
                      . 格式部分:
                      ①嵌套的IF正确地缩进了吗?
                      ②注释准确并有意义吗?
                      ③使用有意义的标号了吗?
                      ④代码基本上与开始时的模块模式一致吗?
                      ⑤遵循全套的编程标准吗?
                      . 入口和出口的连接:
                      ①初始入口和最终出口正确吗?
                      ②当对另一个模块的每一次调用时,全部所需的参数传送给每一个被调用的模块吗?
                      ③被传送的参数值正确地设置了吗?
                      ④对关键的被调用模块的意外情况(如丢失、混乱)有处理吗?
                      . 程序语言的使用:
                      ①使用一个或一组最佳的动词了吗?
                      ②模块中使用完整定义的语言的有限子集了吗?
                      ③使用了适当的跳转语句吗?
                      . 存储器使用:
                      ①每一个域在第一次使用前正确地初始化了吗?
                      ②规定的域正确吗?
                      ③每个域有正确的变量类型声明吗?
                      . 判断和转移:
                      ①判断正确的条件了吗?
                      ②用于判断的是正确的变量吗?
                      ③每个转移目标正确并至少执行一次了吗?
                      . 性能:
                      ①逻辑被最佳地编码了吗?
                      ②提供正式的错误/例外子程序了吗?
                      . 可维护性:
                      ①清单格式适于提高可读性吗?
                      ②标号和子程序符合代码的逻辑意义吗?
                      . 逻辑:
                      ①全部设计已实现了吗?
                      ②代码做的是设计规定的内容吗?
                      ③每一个循环执行正确的次数了吗?
                      . 可靠性:
                      ①对从外部接口采集的数据有确认吗?
                      ②遵循可靠性编程要求了吗?
                      对应于不同的编程语言,代码缺陷检查表的具体内容将会不同。例如,针对广泛使用的C/C++语言,可以参照下表所示的代码缺陷检查表。
                      
                      代码缺陷检查表
                      
                      
                      
               静态结构分析法
               程序的结构形式是白盒测试的主要依据。研究表明程序员38%的时间花费在理解软件系统上,因为代码以文本格式被写入多重文件中,这是很难阅读理解的,需要其他一些东西来帮助人们阅读理解,如各种图表等,而静态结构分析满足了这样的需求。
               在静态结构分析中,测试者通过使用测试工具分析程序源代码的系统结构、数据结构、数据接口、内部控制逻辑等内部结构,生成函数调用关系图、模块控制流图、内部文件调用关系图、子程序表、宏和函数参数表等各类图形图表,可以清晰地标识整个软件系统的组成结构,使其便于阅读与理解,然后可以通过分析这些图表,检查软件有没有存在缺陷或错误。
               其中函数调用关系图通过应用程序中各函数之间的调用关系展示了系统的结构。通过查看函数调用关系图,可以检查函数之间的调用关系是否符合要求,是否存在递归调用,函数的调用层次是否过深,有没有存在孤立的没有被调用的函数。从而可以发现系统是否存在结构缺陷,发现哪些函数是重要的,哪些是次要的,需要使用什么级别的覆盖要求……
               模块控制流图是与程序流程图相类似的由许多节点和连接结点的边组成的一种图形,其中一个节点代表一条语句或数条语句(图中的各种图形符号代表不同的意义),边表示节点间的控制流向,它显示了一个函数的内部逻辑结构。如下图一所示是测试工具Logiscope所使用的基本图例,如下图二(a)、(b)、(c)所示是典型逻辑结构所对应的控制流图基本结构。
               
               Logiscope基本图例
               
               控制流图基本结构
               模块控制流图可以直观地反映出一个函数的内部逻辑结构,通过检查这些模块控制流图,能够很快发现软件的错误与缺陷。
               例如:如下图所示是某GIS软件中某个函数的模块控制流图。
               
               GIS模块控制流图
               该函数的结构存在重大缺陷:首先,我们可以看到在该函数中存在无法执行的死代码,即图中最右边的孤立出口。其次,该函数有多达8个出口,其中一个属于无法到达的出口。由于可能没有在所有的出口进行动态内存的释放与回收操作,因此这样的结构存在内存泄漏的可能。
               如下图所示是某MIS软件中某个函数的模块控制流图。
               
               MIS模块控制流图
               该函数的结构也同样存在重大缺陷:
               首先,该函数有多个出口,存在内存泄漏的可能。其次,该函数结构复杂,有超过10个逻辑判断结点,在这些结点上出现逻辑错误的概率将大大增加,将降低其可靠性,而且过多的逻辑判断结点可能会破坏对CPU操作进行优化的处理,影响其运行性能。
               静态质量度量法
               根据ISO/IEC 9126国际标准的定义,软件的质量包括以下六个方面:
               . 功能性(FUNCTIONALITY);
               . 可靠性(RELIABILITY);
               . 可用性(USABILITY);
               . 有效性(EFFICIENCY);
               . 可维护性(MAINTAINABILITY);
               . 轻便性(PORTABILITY)。
               以ISO 9126质量模型作为基础,我们可以构造质量度量模型,用于评估软件的每个方面。例如,按以下方法构造的质量模型可以度量程序的可维护性(maintainability)。首先,该模型从上到下分为3层:质量因素(factors)、分类标准(criteria)和度量规则(metrics)。其中质量因素对应ISO 9126质量模型的质量特性,分类标准对应ISO 9126质量模型的子特性,度量规则用于规范软件的各种行为属性。其次,按以下方式定义各参数及计算公式。
               . 度量规则(Metrics)。
               度量规则使用了代码行数、注释频度等参数度量软件的各种行为属性,具体参数定义如下表所示。
               
               度量规则参数表
               . 分类标准(criteria)。
               软件的可维护性采用以下四个分类标准来评估:
               ①可分析性(ANALYZABILITY)
               ②可修改性(CHANGEABILITY)
               ③稳定性(STABILITY)
               ④可测性(TESTABILITY)
               每个分类标准由一系列度量规则组成,各个规则分配一个权重,由规则的取值与权重值计算出每个分类标准的取值。各分类标准组成如下表所示。
               
               分类标准组
               各分类标准的结果按以下标准区分等级,如下表一至如下表十二所示。
               function_TESTABILITY=DRCT_CALLS+LEVL+PATH+PARA
               
               function_TESTABILITY的等级划分
               function_STABILITY=NBCALLING+RETU+DRCT_CALLS+PARA
               
               function_STABILITY的等级划分
               function_CHANGEABILITY=PARA+LVAR+VOCF+GOTO
               
               function CHANGEABILITY的等级划分
               function_ANALYZABILITY=VG+STMT+AVGS+COMF
               
               function_ANALYZABILITY的等级划分
               relativeCall_ANALYZABILITY=STRU_CPX+LEVELS
               
               relativeCall ANALYZABILITY的等级划分
               relativeCall_STABILITY=CALL_PATHS+HIER_CPX
               
               relativeCall_STABILITY的等级划分
               relativeCall_TESTABILITY=TESTBTY+CALL_PATHS
               
               relativeCall_TESTABILITY的等级划分
               这样,依据这些标准和最终测试结果,可将代码的质量分成四个等级。
               ①优秀(EXCELLENT):符合本模型框架中的所有规则。
               ②良好(GOOD):未大量偏离模型框架中的规则。
               ③一般(FAIR):违背了模型框架中的大量规则。
               ④较差(POOR):无法保障正常的软件可维护性。
               其中前三者被认为是可以接受的,最后一个等级则是不可接受的。
               . 质量因素(factors)。
               质量因素的取值与分类标准的计算方式相似:依据各分类标准取值组合权重方法来计算,如下表所示。
               
               质量因素权重计算表
               同样,依据质量因素取值,也将其分成四个等级:优秀(EXCELLENT)、良好(GOOD)、一般(FAIR)和较差(POOR),其中前三者被认为是可以接受的,最后一个等级则是不可接受的。
               如下表一和如下表二所示为function_MAINTAINABILITY和relative Call_MINTA-INABILITY的等级划分。
               
               
               function_MAITAINABILITY的等级划分
               
               
               relativeCall_MAINTAINABILITY的等级划分
               将上述质量模型应用于被测程序后,就可以通过量化的数据对软件的质量进行评估了。
               逻辑覆盖法
               白盒测试的动态测试要根据程序的控制结构设计测试用例,其原则是:
               . 保证一个模块中的所有独立路径至少被使用一次;
               . 对所有逻辑值均需测试true和false;
               . 在上下边界及可操作范围内运行所有循环;
               . 检查内部数据结构以确保其有效性。
               但是对一个具有多重选择和循环嵌套的程序,不同的路径数目可能是天文数字。而且即使精确地实现了白盒测试,也不能断言测试过的程序完全正确。如下图所示的穷举测试流程图,其中包括了一个执行达20次的循环,它所包含的不同执行路径数高达520条,假使有这么一个测试程序,对每一条路径进行测试需要1ms,假设一天工作24小时,一年工作365天,若要对它进行穷举测试,也需要3024年的时间。
               
               穷举测试
               以上的情况说明,实现穷举测试的工作量过大,需要的时间过长,实施起来是不现实的。任何软件开发项目都要受到期限、费用、人力和机时等条件的限制,尽管我们以为为了充分揭露程序中的所有隐藏错误,彻底的做法是针对所有可能的数据进行测试,但事实告诉人们,这样做是不可能的。
               在测试阶段既然穷举测试不可行,为了节省时间和资源,提高测试效率,就必须精心设计测试用例,也就是从数量巨大的可用测试用例中精心挑选少量的测试数据,使得采用这些测试数据就能够达到最佳的测试效果。
               本节和下节将介绍几种实用的白盒测试用例设计方法:逻辑覆盖法和基本路径测试法。
               逻辑覆盖是通过对程序逻辑结构的遍历实现程序的覆盖。它是一系列测试过程的总称,这组测试过程逐渐进行越来越完整的通路测试。从覆盖源程序语句的详尽程度分析,逻辑覆盖标准包括以下不同的覆盖标准:语句覆盖(SC)、判定覆盖(DC)、条件覆盖(CC)、条件判定组合覆盖(CDC)、多条件覆盖(MCC)和修正判定条件覆盖(MCDC)。
               为便于理解,我们使用如下所示的程序(用C语言书写),如下图所示的是其流程图。
               [程序]:
               
               
               参考例子流程图
                      语句覆盖(SC)
                      为了暴露程序中的错误,程序中的每条语句至少应该执行一次。因此,语句覆盖(Statement Coverage)的含义是:选择足够多的测试数据,使被测程序中每条语句至少执行一次。
                      为了使上述程序中的每条语句都能够至少执行一次,我们可以构造以下测试用例即可实现:
                      a=T, b=T, c=T。
                      从程序中的每条语句都得到执行这一点看,语句覆盖的方法似乎能够比较全面地检验每一条语句,但是语句覆盖对程序执行逻辑的覆盖很低,这是其最严重的缺陷。
                      假如,这一程序段中判定的逻辑运算有问题,例如,判定的第一个运算符“&&”错写成运算符“||”,或第二个运算符“||”错写成运算符“&&”,这时使用上述的测试用例仍然可以达到100%的语句覆盖,上述的逻辑错误无法发现。
                      因此一般认为语句覆盖是很弱的逻辑覆盖。
                      判定覆盖(DC)
                      比语句覆盖稍强的覆盖标准是判定覆盖(Decision Coverage)。判定覆盖的含义是:设计足够的测试用例,使得程序中的每个判定至少都获得一次“真值”或“假值”,或者说使得程序中的每一个取“真”分支和取“假”分支至少经历一次,因此判定覆盖又称为分支覆盖。
                      除了双值的判定语句外,还有多值判定语句,如case语句,因此判定覆盖更一般的含义是:使得每一个判定获得每一种可能的结果至少一次。
                      以上述代码为例,构造以下测试用例即可实现判定覆盖标准:
                      . a=T, b=T, c=T。
                      . a=F, b=F, c=F。
                      应该注意到,上述两组测试用例不仅满足了判定覆盖,而且满足了语句覆盖,从这一点看,判定覆盖要比语句覆盖更强一些。但是同样地,假如这一程序段中判定的逻辑运算有问题,如下表所示,判定的第一个运算符“&&”错写成运算符“||”或第二个运算符“||”错写成运算符“&&”,这时使用上述的测试用例可以达到100%的判定覆盖,仍然无法发现上述的逻辑错误。因此需要更强的逻辑覆盖标准。
                      
                      判定覆盖
                      条件覆盖(CC)
                      在设计程序中,一个判定语句是由多个条件组合而成的复合判定,在如上图所示参考例子流程图的程序中,判定(a)AND(b OR c)包含了三个条件:a, b和c。为了更彻底地实现逻辑覆盖,可以采用条件覆盖(Condition Coverage)的标准。条件覆盖的含义是:构造一组测试用例,使得每一判定语句中每个逻辑条件的可能值至少满足一次。
                      按照这一定义,上述例子要达到100%的条件覆盖,可以使用以下测试用例:
                      . a=F, b=T, c=F。
                      . a=T, b=F, c=T。
                      仔细分析可以发现,上述用例在满足条件覆盖的同时,把判定的两个分支也覆盖了,这样是否可以说,达到了条件覆盖也就必然实现了判定覆盖呢?
                      假如选用以下的两组测试用例:
                      . a=F, b=T, c=T。
                      . a=T, b=F, c=F。
                      我们会发现覆盖了条件的测试用例并没有覆盖分支,如下表所示。为解决这一矛盾,需要对条件和分支兼顾。
                      
                      条件覆盖
                      条件判定组合覆盖(CDC)
                      条件判定组合覆盖的含义是:设计足够的测试用例,使得判定中每个条件的所有可能(真/假)至少出现一次,并且每个判定本身的判定结果(真/假)也至少出现一次。
                      对于如上图所示的例子,选用以下的两组测试用例可以符合条件判定组合覆盖标准:
                      . a=T, b=T, c=T。
                      . a=F, b=F, c=F。
                      但是条件判定组合覆盖也存在一定的缺陷,例如,判定的第一个运算符“&&”错写成运算符“||”或第二个运算符“||”错写成运算符“&&”,如下表所示,这时使用上述的测试用例仍然可以达到100%的条件判定组合覆盖,上述的逻辑错误无法发现。
                      
                      条件判定组合覆盖
                      多条件覆盖(MCC)
                      多条件覆盖也称条件组合覆盖,它的含义是:设计足够的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。显然满足多条件覆盖的测试用例是一定满足判定覆盖、条件覆盖和条件判定组合覆盖的。
                      对于如下图所示的例子,判定语句中有三个逻辑条件,每个逻辑条件有两种可能取值,因此共有23=8种可能组合,如下表所示的测试用例保证了多条件覆盖。
                      
                      计算最少测试用例数实例
                      
                      多条件覆盖
                      由上可知,当一个程序中判定语句较多时,其条件取值的组合数目是非常大的。
                      修正条件判定覆盖(MCDC)
                      修正条件判定覆盖是由欧美的航空/航天制造厂商和使用单位联合制定的“航空运输和装备系统软件认证标准”,目前在国外的国防、航空航天领域应用广泛。这个覆盖度量需要足够的测试用例来确定各个条件能够影响到包含的判定的结果。它要求满足两个条件:首先,每一个程序模块的入口和出口点都要考虑至少要被调用一次,每个程序的判定到所有可能的结果值要至少转换一次;其次,程序的判定被分解为通过逻辑操作符(and、or)连接的bool条件,每个条件对于判定的结果值是独立的。
                      对于如下图所示的例子,可以设计如下表中的8个用例,在此基础上,按照MCDC的要求选择需要的用例。
                      
                      参考例子流程图
                      
                      修正条件判定覆盖
                      从表中我们可以看出,布尔变量a可以通过用例1和5达到MCDC的要求(用例2和6或用例3和7也可以满足相应要求),变量b可以通过用例2和4达到MCDC的要求,变量c可以通过用例3和4达到MCDC的要求,因此使用用例集{1,2,3,4,5}即可满足MCDC的要求。显而易见,这不是惟一的用例组合。
               基本路径测试法
               上节的例子是个比较简单的程序段,只有两条路径。但在实际问题中,一个不太复杂的程序,其路径的组合都是一个庞大的数字。如下图所示的穷举测试程序竟有520条路径,要在测试中覆盖这样多的路径是不现实的。
               
               穷举测试
               为解决这一难题,需要把覆盖的路径数压缩到一定限度内,例如,程序中的循环体只执行一次。本节介绍的基本路径测试就是这样一种测试方法,它在程序控制流图的基础上,通过分析控制流图的环路复杂性,导出基本可执行路径的集合,然后据此设计测试用例。设计出的测试用例要保证在测试中程序的每一条可执行语句至少执行一次。
                      程序的控制流图
                      控制流图是描述程序控制流的一种图示方式。其中基本的控制结构对应的图形符号如下图所示。在如下图所示的图形符号中,圆圈称为控制流图的一个结点,它表示一个或多个无分支的语句或源程序语句。
                      
                      控制流程图的图形符号
                      如下图(a)所示的是一个程序的流程图,它可以映射成如下图(b)所示的控制流程图。
                      
                      程序流程图和对应的控制流程图
                      这里我们假定在流程图中用菱形框表示的判定条件内没有复合条件,而一组顺序处理框可以映射为一个单一的结点。控制流程图中的箭头(边)表示了控制流的方向,类似于流程图中的流线,一条边必须终止于一个结点,但在选择或者是多分支结构中分支的汇聚处,即使汇聚处没有执行语句也应该添加一个汇聚结点。边和结点圈定的部分叫区域,当对区域计数时,图形外的部分也应记为一个区域。
                      如果判断中的条件表达式是复合条件,即条件表达式是由一个或多个逻辑运算符(or、and、nand和nor)连接的逻辑表达式,则需要改变复合条件的判断为一系列只有单个条件的嵌套的判断。例如,对应如下图所示的复合逻辑下的控制流图(a)的复合条件的判定,应该画成如下图(b)所示的控制流图。条件语句if a and b中条件a和条件b各有一个只有单个条件的判断结点。
                      
                      复合逻辑下的控制流图
                      程序环路复杂性
                      程序的环路复杂性即McCabe复杂性度量,在进行程序的基本路径测试时,从程序的环路复杂性可导出程序基本路径集合中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
                      独立路径是指包括一组以前没有处理的语句或条件的一条路径。从控制流图来看,一条独立路径是至少包含有一条在其他独立路径中从未有过的边的路径。例如,在如下图(b)中所示的控制流图中,一组独立的路径如下:
                      
                      程序流程图和对应的控制流程图
                      path1:1—11;
                      path2:1—2—3—4—5—10—1—11;
                      path3:1—2—3—6—8—9-10—1—11;
                      path4:1—2—3—6—7—9—10—1—11。
                      从此例中可知,一条新的路径必须包含有一条新边。路径1—2—3—4—5—10—1—2—3—6—8—9—10—1—11不能作为一条独立路径,因为它只是前面已经说明了的路径的组合,没有通过新的边。
                      路径path1、path2、path3和path4组成了如上图(b)所示控制流图的一个基本路径集。只要设计出的测试用例能够确保这些基本路径的执行,就可以使得程序中的每个可执行语句至少执行一次,每个条件的取真和取假分支也能得到测试。基本路径集不是惟一的,对于给定的控制流图,可以得到不同的基本路径集。
                      通常环路复杂性还可以简单地定义为控制流图的区域数。这样对于如上图(b)所示的控制流图,它有4个区域,环路复杂性V(G)=4,它是构成基本路径集的独立路径数的上界,可以据此得到应该设计的测试用例的数目。
                      基本路径测试法步骤
                      基本路径测试法适用于模块的详细设计及源程序,其主要步骤如下:
                      . 以详细设计或源代码作为基础,导出程序的控制流图;
                      . 计算得到的控制流图G的环路复杂性V(G);
                      . 确定线性无关的路径的基本集;
                      . 生成测试用例,确保基本路径集中每条路径的执行。
                      下面以一个求平均值的过程averagy为例,说明测试用例的设计过程。用PDL语言描述的averagy过程如下所示。
                      
                      
                             以详细设计或源代码作为基础,导出程序的控制流图
                             利用在如下图一所示的控制流图的图形符号、如下图二所示的程序流图和对应的控制流图和如下图三所示的复合逻辑下的控制流图给出的符号和构造规则生成控制流图。对于上述过程,对将要映射为对应控制流图中一个结点的PDL语句或语句组,加上用数字表示的标号。加了标号的PDL程序及对应的控制流图如下图四和如下图五所示。
                             
                             控制流程图的图形符号
                             
                             程序流程图和对应的控制流程图
                             
                             复合逻辑下的控制流图
                             
                             对averagy过程定义结点
                             计算得到的控制流图G的环路复杂性V(G)
                             利用在前面给出的计算控制流图环路复杂性的方法,算出控制流图G的环路复杂性。如果一开始就知道判断结点的个数,甚至不必画出整个控制流图,就可以计算出该图的环路复杂性的值。对于如下图所示的控制流图,可以算出:
                             
                             averagy过程的控制流图
                             V(G)=6(区域数)=5(判断结点数)+1=6。
                             确定线性无关路径的基本集
                             针对如上图所示的averagy过程的控制流图计算出的环路复杂性的值,就是该图已有的线性无关基本路径集中路径数目。该图所有的6条路径如下所示。
                             [path1]1—2—10—11—13
                             [path2]1—2—10—12—13
                             [path3]1—2—3—10—11—13
                             [path4]1—2—3—4—5—8—9—2…
                             [path5]1—2—3—4—5—6—8—9—2…
                             [path6]1—2—3—4—5—6—7—8—9—2…
                             路径4、5、6后面的省略号(…)表示在控制结构中以后剩下的路径是可选择的。在很多情况下,标识判断结点,常常能够有效地帮助导出测试用例。在上例中,结点2、3、5、6和10都是判断结点。
                             (4)生成测试用例,确保基本路径集中每条路径的执行
                             根据判断结点给出的条件,选择适当的数据以保证某一条路径可以被测试到。满足上述基本路径集的测试用例如下所示。
                             [path1]输入数据:value[k]=有效输入,限于k
                             value[i]=-999,当2≤i≤100。
                             预期结果:n个值的正确的平均值、正确的总计数。
                             注意:不能孤立地进行测试,应当作为路径4、5、6测试的一部分来测试。
                             [path2]输入数据:value[1]=-999;
                             预期结果:平均值=-999,总计数取初始值。
                             [path3]输入数据:试图处理101个或更多的值,而前100个应当是有效的值;
                             预期结果:与测试用例1相同。
                             [path4]输入数据:value[i]=有效输入,且i<100;
                             value[k]<最小值,当k
                             预期结果:n个值的正确的平均值、正确的总计数。
                             [path5]输入数据:value[i]=有效输入,且i<100;
                             value[k]>最大值,当k≤i时;
                             预期结果:n个值的正确的平均值、正确的总计数。
                             [path6]输入数据:value[i]=有效输入,且i<100
                             预期结果:n个值的正确的平均值、正确的总计数。
                             每个测试用例执行之后,与预期结果进行比较。如果所有测试用例都执行完毕,则可以确信程序中所有的可执行语句至少被执行了一次。但是必须注意的是,一些独立的路径(如此例中的路径1),往往不是完全孤立的,有时它是程序正常的控制流的一部分,这时,这些路径的测试可以是另一条路径测试的一部分。
               其他白盒测试方法
                      域测试
                      域测试(Domain Testing)是一种基于程序结构的测试方法。Howden曾对程序中出现的错误进行分类,他将程序错误分为域错误、计算型错误和丢失路径错误三种,这是相对于执行程序的路径来说的。我们知道,每条执行路径对应于输入域的一类情况,是程序的一个子计算。如果程序的控制流有错误,对于某一特定的输入可能执行的是一条错误路径,这种错误称为路径错误,也叫做域错误。如果对于特定输入执行的是正确路径,但由于赋值语句的错误致使输出结果不正确,则称此为计算型错误。
                      另外一类错误是丢失路径错误。它是由于程序中的某处少了一个判定谓词而引起的。域测试主要是针对域错误进行的程序测试。
                      域测试的“域”是指程序的输入空间。域测试方法基于对输入空间的分析。自然,任何一个被测程序都有一个输入空间。测试的理想结果就是检验输入空间中的每一个输入元素是否都产生正确的结果。而输入空间又可分为不同的子空间,每一子空间对应一种不同的计算。在考察被测试程序的结构以后,我们就会发现,子空间的划分是由程序中分支语句中的谓词决定的。输入空间的一个元素,经过程序中某些特定语句的执行而结束(当然也可能出现无限循环而无出口),那都是满足了这些特定语句被执行所要求的条件的。
                      域测试正是在分析输入域的基础上,选择适当的测试点以后进行测试的。
                      域测试有两个致命的弱点,一是为进行域测试对程序提出的限制过多,二是当程序存在很多路径时,所需的测试点也就很多。
                      符号测试
                      符号测试的基本思想是允许程序的输入不仅仅是具体的数值数据,而且包括符号值,这一方法也是因此而得名的。这里所说的符号值可以是基本符号变量值,也可以是这些符号变量值的一个表达式。这样,在执行程序过程中以符号的计算代替了普通测试执行中对测试用例的数值计算。所得到的结果自然是符号公式或是符号谓词。更明确地说,普通测试执行的是算术运算,符号测试执行的则是代数运算。因此符号测试可以认为是普通测试的一个自然的扩充。
                      符号测试可以看作是程序测试和程序验证的一个折衷方法。一方面,它沿用了传统的程序测试方法,通过运行被测程序来验证它的可靠性。另一方面,由于一次符号测试的结果代表了一大类普通测试的运行结果,实际上是证明了程序接受此类输入后,所得输出是正确的还是错误的。最为理想的情况是,程序中仅有有限的几条执行路径。如果对这有限的几条路径都完成了符号测试,我们就能较有把握地确认程序的正确性了。
                      从符号测试方法的使用来看,问题的关键在于开发出比传统的编译器功能更强,能够处理符号运算的编译器和解释器。
                      目前符号测试存在一些未得到圆满解决的问题,如下所示。
                      . 分支问题。
                      当采用符号执行方法进行到某一分支点处,分支谓词是符号表达式,这种情况下通常无法决定谓词的取值,也就不能决定分支的走向,需要测试人员做人工干预,或是执行树的方法进行下去。如果程序中有循环,而循环次数又决定于输入变量,那就无法确定循环的次数。
                      . 二义性问题。
                      数据项的符号值可能是有二义性的。这种情况通常出现在带有数组的程序中。
                      我们来看以下的程序段:
                      
                      如果I=J,则C=3;否则,C=2+A。但由于使用符号值运算,这时无法知道I是否等于J。
                      . 大程序问题。
                      符号测试中经常要处理符号表达式。随着符号执行的继续,一些变量的符号表达式会越来越庞大。特别是当符号执行树很大,分支点很多时,路径条件本身变成一个非常长的表达式。如果能够有办法将其化简,自然会带来很大好处。但如果找不到化简的办法,那将给符号测试的时间和运行空间带来大幅度的增长,甚至使整个问题的解决遇到难以克服的困难。
                      Z路径覆盖
                      分析程序中的路径是指:检验程序从入口开始,执行过程中经历的各个语句,直到出口。这是白盒测试最为典型的问题。着眼于路径分析的测试可称为路径测试。完成路径测试的理想情况是做到路径覆盖。对于比较简单的小程序实现路径覆盖是可能做到的。但是如果程序中出现多个判断和多个循环,可能的路径数目将会急剧增长,达到天文数字,以至于实现完全路径覆盖是不可能做到的。
                      为了解决这一问题,我们必须舍掉一些次要因素,对循环机制进行简化,从而极大地减少路径的数量,使得覆盖这些有限的路径成为可能。我们称简化循环意义下的路径覆盖为Z路径覆盖。
                      这里所说的对循环化简,是指限制循环的次数。无论循环的形式和实际执行循环体的次数多少,我们只考虑循环一次和零次两种情况。即只考虑执行时进入循环体一次和跳过循环体这两种情况。如下图(a)和(b)所示表示了两种最典型的循环控制结构。前者先作判断,循环体B可能执行(假定只执行一次),也可能不执行。这就如同如下图(c)所示的条件选择结构一样。后者先执行循环体B(假定也执行一次),再经判断转出,其效果也与(c)中给出的条件选择结构只执行右支的效果一样。
                      
                      循环结构简化成选择结构
                      对于程序中的所有路径可以用路径树来表示,具体表示方法这里忽略。当得到某一程序的路径树后,从其根结点开始,一次遍历,再回到根结点时,把所经历的叶结点名排列起来,就得到一个路径。如果我们设法遍历了所有的叶结点,就得到了所有的路径。
                      当得到所有的路径后,生成每个路径的测试用例,就可以做到Z路径覆盖测试。
                      程序变异
                      程序变异方法与前面提到的结构测试和功能测试都不一样,它是一种错误驱动测试。所谓错误驱动测试,是指该方法是针对某类特定程序错误的。经过多年的测试理论研究和软件测试的实践,人们逐渐发现要想找出程序中所有的错误几乎是不可能的。比较现实的解决办法是将错误的搜索范围尽可能地缩小,以利于专门测试某类错误是否存在。这样做的好处在于,便于集中目标于对软件危害最大的可能错误,而暂时忽略对软件危害较小的可能错误。这样可以取得较高的测试效率,并降低测试的成本。
                      错误驱动测试主要有两种,即程序强变异和程序弱变异。为便于测试人员使用变异方法,一些变异测试工具被开发出来。关于程序变异测试方法,请参见清华大学出版社出版的《软件测试技术》一书。
 
        代码检查法
        代码检查包括桌面检查、代码审查和走查等,主要检查代码和设计的一致性,代码对标准的遵循、可读性,代码逻辑表达的正确性,代码结构的合理性等方面;发现违背程序编写标准的问题,程序中不安全、不明确和模糊的部分,找出程序中不可移植部分、违背程序编程风格的问题,包括变量检查、命名和类型审查、程序逻辑审查、程序语法检查和程序结构检查等内容。
               代码检查方式
                      桌面检查
                      这是一种传统的检查方法,由程序员检查自己编写的程序。程序员在程序通过编译之后,对源程序代码进行分析、检验,并补充相关的文档,目的是发现程序中的错误。
                      由于程序员熟悉自己的程序及其程序设计风格,桌面检查由程序员自己进行可以节省很多的检查时间,但应避免主观片面性。
                      代码审查
                      代码审查是由若干程序员和测试员组成一个审查小组,通过阅读、讨论和争议,对程序进行静态分析的过程。
                      代码审查分两步:第一步,小组负责人提前把设计规格说明书、控制流程图、程序文本及有关要求、规范等分发给小组成员,作为审查的依据。小组成员在充分阅读这些材料后,进入审查的第二步,召开程序审查会。在会上,首先由程序员逐句讲解程序的逻辑。在此过程中,程序员或其他小组成员可以提出问题,展开讨论,审查错误是否存在。实践表明,程序员在讲解过程中能发现许多原来自己没有发现的错误,而讨论和争议则促进了问题的暴露。例如,对某个局部性小问题修改方法的讨论,可能发现与之牵连的其他问题,甚至涉及到模块的功能说明、模块间接口和系统总体结构的大问题,从而导致对需求的重定义、重设计和重验证,进而大大改善了软件质量。
                      在会前,应当给审查小组每个成员准备一份常见错误的清单,把以往所有可能发生的常见错误罗列出来,供与会者对照检查,以提高审查的实效。
                      这个常见错误清单也称为检查表,它把程序中可能发生的各种错误进行分类,对每一类列举出尽可能多的典型错误,然后把它们制成表格,供再审查时使用。
                      走查
                      走查与代码审查基本相同,其过程分为两步。
                      第一步也是把材料先发给走查小组每个成员,让他们认真研究程序,然后再开会。开会的程序与代码审查不同,不是简单地读程序和对照错误检查表进行检查,而是让与会者“充当”计算机,即首先由测试组成员为所测程序准备一批有代表性的测试用例,提交给走查小组。走查小组开会,集体扮演计算机角色,让测试用例沿程序的逻辑运行一遍,随时记录程序的踪迹,供分析和讨论用。
                      人们借助测试用例的媒介作用,对程序的逻辑和功能提出各种疑问,结合问题开展热烈的讨论和争议,能够发现更多的问题。
                      代码检查应在编译和动态测试之前进行,在检查前,应准备好需求描述文档、程序设计文档、程序的源代码清单、代码编码标准和代码缺陷检查表等。
                      在实际使用中,代码检查能快速找到缺陷,发现30%~70%的逻辑设计和编码缺陷,而且代码检查看到的是问题本身而非征兆。但是代码检查非常耗费时间,而且代码检查需要知识和经验的积累。
                      代码检查可以使用测试软件进行自动化测试,以利于提高测试效率,降低劳动强度,或者使用人工进行测试,以充分发挥人力的逻辑思维能力。
               代码检查项目
               . 检查变量的交叉引用表:重点是检查未说明的变量和违反了类型规定的变量;还要对照源程序,逐个检查变量的引用、变量的使用序列、临时变量在某条路径上的重写情况,局部变量、全局变量与特权变量的使用。
               . 检查标号的交叉引用表:验证所有标号的正确性,检查所有标号的命名是否正确,转向指定位置的标号是否正确。
               . 检查子程序、宏、函数:验证每次调用与所调用位置是否正确,确认每次所调用的子程序、宏、函数是否存在,检验调用序列中调用方式与参数顺序、个数、类型上的一致性。
               . 等价性检查:检查全部等价变量的类型的一致性,解释所包含的类型差异。
               . 常量检查:确认常量的取值和数制、数据类型,检查常量每次引用同它的取值、数制和类型的一致性。
               . 标准检查:用标准检查工具软件或手工检查程序中违反标准的问题。
               . 风格检查:检查发现程序在设计风格方面的问题。
               . 比较控制流:比较由程序员设计的控制流图和由实际程序生成的控制流图,寻找和解释每个差异,修改文档并修正错误。
               . 选择、激活路径:在程序员设计的控制流图上选择路径,再到实际的控制流图上激活这条路径。如果选择的路径在实际控制流图上不能被激活,则源程序可能有错。
               . 对照程序的规格说明,详细阅读源代码,逐字逐句进行分析和思考,比较实际的代码和期望的代码,从它们的差异中发现程序的问题和错误。
               . 补充文档:桌面检查的文档是一种过渡性的文档,不是公开的正式文档。通过编写文档,也是对程序的一种下意识的检查和测试,可以帮助程序员发现和抓住更多的错误。管理部门也可以通过审查桌面检查文档,了解模块的质量、完全性、测试方法和程序员的能力。
               根据检查项目可以编制代码规则、规范和检查表等作为测试用例,如编码规范、代码检查规则、缺陷检查表等。
               编码规范
               编码规范是程序编写过程中必须遵循的规则,一般会详细规定代码的语法规则、语法格式等,如下表所示。
               
               编码规范
               
               
               
               代码检查规则
               在代码检查中,需要依据被测软件的特点,选用适当的标准与规则规范。在使用测试软件进行自动化代码检查时,测试工具一般会内置许多的编码规则,例如,Parasoft公司用于C/C++语言测试的C++Test,内置了5类规则:通用规则、C++编码规则、C编码规则、Meyers-Klaus规则和自定义规则。我们需要根据编程语言以及被测程序的特点,挑选适当的规则进行检查。
               例如,在测试中,可以选择下表中的规则进行自动化测试,在此基础上使用桌面检查、代码走查、代码审查等人工检查的方法仔细检查程序的结构、逻辑等方面的缺陷。
               
               代码检查规则
               缺陷检查表
               在进行人工代码检查时,代码缺陷检查表是我们用到的测试用例。
               代码缺陷检查表中一般包括容易出错的地方和在以往的工作中遇到的典型错误,如下所示。
               . 格式部分:
               ①嵌套的IF正确地缩进了吗?
               ②注释准确并有意义吗?
               ③使用有意义的标号了吗?
               ④代码基本上与开始时的模块模式一致吗?
               ⑤遵循全套的编程标准吗?
               . 入口和出口的连接:
               ①初始入口和最终出口正确吗?
               ②当对另一个模块的每一次调用时,全部所需的参数传送给每一个被调用的模块吗?
               ③被传送的参数值正确地设置了吗?
               ④对关键的被调用模块的意外情况(如丢失、混乱)有处理吗?
               . 程序语言的使用:
               ①使用一个或一组最佳的动词了吗?
               ②模块中使用完整定义的语言的有限子集了吗?
               ③使用了适当的跳转语句吗?
               . 存储器使用:
               ①每一个域在第一次使用前正确地初始化了吗?
               ②规定的域正确吗?
               ③每个域有正确的变量类型声明吗?
               . 判断和转移:
               ①判断正确的条件了吗?
               ②用于判断的是正确的变量吗?
               ③每个转移目标正确并至少执行一次了吗?
               . 性能:
               ①逻辑被最佳地编码了吗?
               ②提供正式的错误/例外子程序了吗?
               . 可维护性:
               ①清单格式适于提高可读性吗?
               ②标号和子程序符合代码的逻辑意义吗?
               . 逻辑:
               ①全部设计已实现了吗?
               ②代码做的是设计规定的内容吗?
               ③每一个循环执行正确的次数了吗?
               . 可靠性:
               ①对从外部接口采集的数据有确认吗?
               ②遵循可靠性编程要求了吗?
               对应于不同的编程语言,代码缺陷检查表的具体内容将会不同。例如,针对广泛使用的C/C++语言,可以参照下表所示的代码缺陷检查表。
               
               代码缺陷检查表
               
               
               
 
        静态结构分析法
        程序的结构形式是白盒测试的主要依据。研究表明程序员38%的时间花费在理解软件系统上,因为代码以文本格式被写入多重文件中,这是很难阅读理解的,需要其他一些东西来帮助人们阅读理解,如各种图表等,而静态结构分析满足了这样的需求。
        在静态结构分析中,测试者通过使用测试工具分析程序源代码的系统结构、数据结构、数据接口、内部控制逻辑等内部结构,生成函数调用关系图、模块控制流图、内部文件调用关系图、子程序表、宏和函数参数表等各类图形图表,可以清晰地标识整个软件系统的组成结构,使其便于阅读与理解,然后可以通过分析这些图表,检查软件有没有存在缺陷或错误。
        其中函数调用关系图通过应用程序中各函数之间的调用关系展示了系统的结构。通过查看函数调用关系图,可以检查函数之间的调用关系是否符合要求,是否存在递归调用,函数的调用层次是否过深,有没有存在孤立的没有被调用的函数。从而可以发现系统是否存在结构缺陷,发现哪些函数是重要的,哪些是次要的,需要使用什么级别的覆盖要求……
        模块控制流图是与程序流程图相类似的由许多节点和连接结点的边组成的一种图形,其中一个节点代表一条语句或数条语句(图中的各种图形符号代表不同的意义),边表示节点间的控制流向,它显示了一个函数的内部逻辑结构。如下图一所示是测试工具Logiscope所使用的基本图例,如下图二(a)、(b)、(c)所示是典型逻辑结构所对应的控制流图基本结构。
        
        Logiscope基本图例
        
        控制流图基本结构
        模块控制流图可以直观地反映出一个函数的内部逻辑结构,通过检查这些模块控制流图,能够很快发现软件的错误与缺陷。
        例如:如下图所示是某GIS软件中某个函数的模块控制流图。
        
        GIS模块控制流图
        该函数的结构存在重大缺陷:首先,我们可以看到在该函数中存在无法执行的死代码,即图中最右边的孤立出口。其次,该函数有多达8个出口,其中一个属于无法到达的出口。由于可能没有在所有的出口进行动态内存的释放与回收操作,因此这样的结构存在内存泄漏的可能。
        如下图所示是某MIS软件中某个函数的模块控制流图。
        
        MIS模块控制流图
        该函数的结构也同样存在重大缺陷:
        首先,该函数有多个出口,存在内存泄漏的可能。其次,该函数结构复杂,有超过10个逻辑判断结点,在这些结点上出现逻辑错误的概率将大大增加,将降低其可靠性,而且过多的逻辑判断结点可能会破坏对CPU操作进行优化的处理,影响其运行性能。
 
        静态质量度量法
        根据ISO/IEC 9126国际标准的定义,软件的质量包括以下六个方面:
        . 功能性(FUNCTIONALITY);
        . 可靠性(RELIABILITY);
        . 可用性(USABILITY);
        . 有效性(EFFICIENCY);
        . 可维护性(MAINTAINABILITY);
        . 轻便性(PORTABILITY)。
        以ISO 9126质量模型作为基础,我们可以构造质量度量模型,用于评估软件的每个方面。例如,按以下方法构造的质量模型可以度量程序的可维护性(maintainability)。首先,该模型从上到下分为3层:质量因素(factors)、分类标准(criteria)和度量规则(metrics)。其中质量因素对应ISO 9126质量模型的质量特性,分类标准对应ISO 9126质量模型的子特性,度量规则用于规范软件的各种行为属性。其次,按以下方式定义各参数及计算公式。
        . 度量规则(Metrics)。
        度量规则使用了代码行数、注释频度等参数度量软件的各种行为属性,具体参数定义如下表所示。
        
        度量规则参数表
        . 分类标准(criteria)。
        软件的可维护性采用以下四个分类标准来评估:
        ①可分析性(ANALYZABILITY)
        ②可修改性(CHANGEABILITY)
        ③稳定性(STABILITY)
        ④可测性(TESTABILITY)
        每个分类标准由一系列度量规则组成,各个规则分配一个权重,由规则的取值与权重值计算出每个分类标准的取值。各分类标准组成如下表所示。
        
        分类标准组
        各分类标准的结果按以下标准区分等级,如下表一至如下表十二所示。
        function_TESTABILITY=DRCT_CALLS+LEVL+PATH+PARA
        
        function_TESTABILITY的等级划分
        function_STABILITY=NBCALLING+RETU+DRCT_CALLS+PARA
        
        function_STABILITY的等级划分
        function_CHANGEABILITY=PARA+LVAR+VOCF+GOTO
        
        function CHANGEABILITY的等级划分
        function_ANALYZABILITY=VG+STMT+AVGS+COMF
        
        function_ANALYZABILITY的等级划分
        relativeCall_ANALYZABILITY=STRU_CPX+LEVELS
        
        relativeCall ANALYZABILITY的等级划分
        relativeCall_STABILITY=CALL_PATHS+HIER_CPX
        
        relativeCall_STABILITY的等级划分
        relativeCall_TESTABILITY=TESTBTY+CALL_PATHS
        
        relativeCall_TESTABILITY的等级划分
        这样,依据这些标准和最终测试结果,可将代码的质量分成四个等级。
        ①优秀(EXCELLENT):符合本模型框架中的所有规则。
        ②良好(GOOD):未大量偏离模型框架中的规则。
        ③一般(FAIR):违背了模型框架中的大量规则。
        ④较差(POOR):无法保障正常的软件可维护性。
        其中前三者被认为是可以接受的,最后一个等级则是不可接受的。
        . 质量因素(factors)。
        质量因素的取值与分类标准的计算方式相似:依据各分类标准取值组合权重方法来计算,如下表所示。
        
        质量因素权重计算表
        同样,依据质量因素取值,也将其分成四个等级:优秀(EXCELLENT)、良好(GOOD)、一般(FAIR)和较差(POOR),其中前三者被认为是可以接受的,最后一个等级则是不可接受的。
        如下表一和如下表二所示为function_MAINTAINABILITY和relative Call_MINTA-INABILITY的等级划分。
        
        
        function_MAITAINABILITY的等级划分
        
        
        relativeCall_MAINTAINABILITY的等级划分
        将上述质量模型应用于被测程序后,就可以通过量化的数据对软件的质量进行评估了。
 
        测试方法
        根据是否执行软件,将软件测试方法分为静态测试和动态测试。动态测试是建立在程序的执行过程中,根据是否要求了解被测对象的内部,分为黑盒测试和白盒测试。
               静态测试和动态测试
                      静态测试
                      静态测试方法包括检查单和静态分析方法,对软件文档的静态测试方法主要是以检查单的形式进行文档审查,而对软件代码的静态测试方法一般采用代码审查、代码走查和静态分析的形式进行。
                      静态分析是一种对代码的机械性和程序化的特性分析方法。一般包括控制流分析、数据流分析、接口分析和表达式分析。
                      代码审查是检查代码和设计的一致性、代码执行标准的情况、代码逻辑表达的正确性、代码结构的合理性以及代码的可读性。代码审查应根据所使用的语言和编码规范确定审查所用的检查单,检查单的设计或采用应经过评审。
                      代码走查是由测试人员组成小组,准备一批有代表性的测试用例,集体扮演计算机的角色,按照程序的逻辑,逐步运行测试用例,查找被测软件缺陷。代码走查应由测试人员集体阅读讨论程序,是用“人脑”执行测试用例并检查程序。
                      对于规模较小、安全性要求很高的代码也可进行形式化证明。静态分析常需要使用软件工具进行。
                      静态测试的特点有:不必设计在计算机上执行的测试用例;可充分发挥人的逻辑思维优势;不需特别条件,容易开展;发现错误的同时也就定位了错误,不需作额外的错误定位工作。
                      动态测试
                      动态测试是建立在程序的执行过程中,根据是否对被测对象内部的了解,分为黑盒测试和白盒测试。
                      黑盒测试是一种按照软件功能说明设计测试数据的技术,不考虑程序内部结构和编码结构,也不需考虑程序的语句及路径,只需了解输入/输出之间的关系,依靠这一关系和软件功能说明确定测试数据,判定测试结果的正确性。黑盒测试又称功能测试、数据驱动测试或基于需求的测试。
                      白盒测试是一种按照程序内部逻辑结构和编码结构设计测试数据的技术,可以看到程序内部结构,并根据内部结构设计测试数据,使程序中的每个语句、每个条件分支、每个控制路径的覆盖情况都在测试中受到检验。白盒测试又称结构测试、逻辑测试或基于程序的测试。
                      动态测试的特点有:实际运行被测程序;必须设计测试用例来运行;测试结果分析工作量大,测试工作费时、费力;投入人员多、设备多,处理数据多,要求有较好的管理和工作规程。
                      在软件动态测试过程中,应采用适当的测试方法,实现测试要求。配置项测试和系统测试一般采用黑盒测试方法;部件测试一般主要采用黑盒测试方法,辅助以白盒测试方法;单元测试一般采用白盒测试方法,辅助以黑盒测试方法。
               黑盒测试
               黑盒测试方法一般采用功能分解、等价类划分、边界值分析、判定表、因果图、随机测试、猜错法和正交试验法等。
                      功能分解
                      功能分解是将需求规格说明中每一个功能加以分解,确保各个功能被全面地测试。功能分解是一种较常用的方法。
                      步骤如下:
                      (1)使用程序设计中的功能抽象方法把程序分解为功能单元。
                      (2)使用数据抽象方法产生测试每个功能单元的数据。
                      功能抽象中程序被看成一种抽象的功能层次,每个层次可标识被测试的功能,层次结构中的某一功能有由其下一层功能定义。按照功能层次进行分解,可以得到众多的最低层次的子功能,以这些子功能为对象,进行测试用例设计。
                      数据抽象中,数据结构可以由抽象数据类型的层次图来描述,每个抽象数据类型有其取值集。程序的每一个输入和输出量的取值集合用数据抽象来描述。
                      等价类划分
                      等价类划分是在分析需求规格说明的基础上,把程序的输入域划分成若干部分,然后在每部分中选取代表性数据形成测试用例。
                      步骤如下:
                      (1)划分有效等价类:对规格说明是有意义、合理的输入数据所构成的集合。
                      (2)划分无效等价类:对规格说明是无意义、不合理的输入数据所构成的集合。
                      (3)为每一个等价类定义一个唯一的编号。
                      (4)为每一个等价类设计一组测试用例,确保覆盖相应的等价类。
                      边界值分析
                      边界值分析是针对边界值进行测试的。使用等于、小于或大于边界值的数据对程序进行测试的方法就是边界值分析方法。
                      步骤如下:
                      (1)通过分析需求规格说明,找出所有可能的边界条件。
                      (2)对每一个边界条件,给出满足和不满足边界值的输入数据。
                      (3)设计相应的测试用例。
                      对满足边界值的输入可以发现计算错误,对不满足的输入可以发现域错误。该方法会为其他测试方法补充一些测试用例,绝大多数测试都会用到本方法。
                      判定表
                      判定表由四部分组成:条件桩、条件条目、动作桩、动作条目。任何一个条件组合的取值及其相应要执行的操作构成规则,条目中的每一列是一条规则。
                      条件引用输入的等价类,动作引用被测软件的主要功能处理部分,规则就是测试用例。
                      建立并优化判定表,把判定表中每一列表示的情况写成测试用例。
                      该方法的使用有以下要求:
                      (1)需求规格说明以判定表形式给出,或是很容易转换成判定表。
                      (2)条件的排列顺序不会影响执行哪些操作。
                      (3)规则的排列顺序不会影响执行哪些操作。
                      (4)每当某一规则的条件已经满足,并确定要执行的操作后,不必检验别的规则。
                      (5)如果某一规则的条件的满足,将执行多个操作,这些操作的执行与顺序无关。
                      因果图
                      因果图方法是通过画因果图,把用自然语言描述的功能说明转换为判定表,然后为判定表的每一列设计一个测试用例。
                      步骤如下:
                      (1)分析需求规格说明,引出原因(输入条件)和结果(输出结果),并给每个原因和结果赋予一个标识符。
                      (2)分析需求规格说明中语义的内容,并将其表示成连接各个原因和各个结果的“因果图”。
                      (3)在因果图上标明约束条件。
                      (4)通过跟踪因果图中的状态条件,把因果图转换成有限项的判定表。
                      (5)把判定表中每一列表示的情况生成测试用例。
                      如果需求规格说明中含有输入条件的组合,宜采用本方法。有些软件的因果图可能非常庞大,根据因果图得到的测试用例数目非常多,此时不宜使用本方法。
                      随机测试
                      随机测试指测试输入数据是在所有可能输入值中随机选取的。测试人员只需规定输入变量的取值区间,在需要时提供必要的变换机制,使产生的随机数服从预期的概率分布。该方法获得预期输出比较困难,多用于可靠性测试和系统强度测试。
                      猜错法
                      猜错法是有经验的测试人员,通过列出可能有的错误和易错情况表,写出测试用例的方法。
                      正交实验法
                      正交实验法是从大量的实验点挑出适量的、有代表性的点,应用正交表,合理地安排实验的一种实验设计方法。
                      利用正交实验法来设计测试用例时,首先要根据被测软件的需求规格说明找出影响功能实现的操作对象和外部因素,把它们当作因子,而把各个因子的取值当作状态,生成二无的因素分析表。然后,利用正交表进行各因子的状态的组合,构造有效的测试输入数据集,并由此建立因果图。这样得出的测试用例的数目将大大减少。
               白盒测试
               白盒测试方法一般包括控制流测试(语句覆盖测试、分支覆盖测试、条件覆盖测试、修订的条件/判定覆盖MC/DC、条件组合覆盖测试、路径覆盖测试)、数据流测试、程序变异、程序插桩、域测试和符号求值等。
                      控制流测试
                      控制流测试依据控制流程图产生测试用例,通过对不同控制结构成分的测试验证程序的控制结构。所谓验证某种控制结构即指使这种控制结构在程序运行中得到执行,也称这一过程为覆盖。以下介绍几种覆盖:
                      (1)语句覆盖。语句覆盖要求设计适当数量的测试用例,运行被测程序,使得程序中每一条语句至少被遍历,语句覆盖在测试中主要发现错误语句。
                      (2)分支覆盖。分支覆盖要求设计适当数量的测试用例,运行被测程序,使得程序中每个真值分支和假值分支至少执行一次,分支覆盖也称判定覆盖。
                      (3)条件覆盖。条件覆盖要求设计适当数量的测试用例,运行被测程序,使得每个判断中的每个条件的可能取值至少满足一次。
                      (4)修订的条件/判定覆盖(MC/DC——Modified Condition/Decision Coverage)。修订的条件/判定覆盖要求设计适当数量的测试用例,运行被测程序,使得每个判定中的每个条件都曾独立的影响判定的结果至少一次(独立影响意思是在其他的条件不变的情况下,只改变一个条件,就可影响整个判定的值)。
                      对安全性要求比较高的软件,一般采用此覆盖要求。此覆盖要求在测试用例的效率和数量之间较为平衡。
                      (5)条件组合覆盖。条件组合覆盖要求设计适当数量的测试用例,运行被测程序,使得每个判断中条件的各种组合至少出现一次,这种方法包含了“分支覆盖”和“条件覆盖”的各种要求。
                      (6)路径覆盖。路径覆盖要求设计适当数量的测试用例,运行被测程序,使得程序沿所有可能的路径执行,较大程序的路径可能很多,所以在设计测试用例时,要简化循环次数。
                      以上各种覆盖的控制流测试步骤如下:
                      (1)将程序流程图转换成控制流图。
                      (2)经过语法分析求得路径表达式。
                      (3)生成路径树。
                      (4)进行路径编码。
                      (5)经过译码得到执行的路径。
                      (6)通过路径枚举产生特定路径的测试用例。
                      控制流图是描述程序控制流的一种图示方式,它由结点和定向边构成。控制流图的结点代表一个基本块,定向边代表控制流的方向。其中要特别注意的是,如果判断中的条件表达式是复合条件,即条件表达式是由一个或多个逻辑运算符连接的逻辑表达式,则需要改变复合条件的判断为一系列单个条件的嵌套的判断。控制流图的基本结构如下图所示。
                      
                      控制流图基本结构
                      数据流测试
                      数据流测试是用控制流程图对变量的定义和引用进行分析,查找出未定义的变量或定义了而未使用的变量,这些变量可能是拼错的变量、变量混淆或丢失了语句。数据流测试一般使用工具进行。
                      数据流测试通过一定的覆盖准则,检查程序中每个数据对象的每次定义、使用和消除的情况。
                      数据流测试步骤:
                      (1)将程序流程图转换成控制流图。
                      (2)在每个链路上标注对有关变量的数据操作的操作符号或符号序列。
                      (3)选定数据流测试策略。
                      (4)根据测试策略得到测试路径。
                      (5)根据路径可以获得测试输入数据和测试用例。
                      动态数据流异常检查在程序运行时执行,获得的是对数据对象的真实操作序列,克服了静态分析检查的局限,但动态方式检查是沿与测试输入有关的一部分路径进行的,检查的全面性和程序结构覆盖有关。
                      程序变异
                      程序变异是一种错误驱动测试,是为了查出被测软件在做过其他测试后还剩余一些的小错误。本方法应用于测试工具。
                      程序插装
                      程序插装是向被测程序中插入操作以实现测试目的方法。程序插装不应该影响被测程序的运行过程和功能。
                      有很多的工具有程序插装功能。由于数据记录量大,手工进行将是一件很烦琐的事。
                      域测试
                      域测试是要判别程序对输入空间的划分是否正确。该方法限制太多,使用不方便,供有特殊要求的测试使用。
                      符号求值
                      符号求值是允许数值变量取“符号值”以及数值。符号求值可以检查公式的执行结果是否达到程序预期的目的;也可以通过程序的符号执行,产生出程序的路径,用于产生测试数据。符号求值最好使用工具,在公式分支较少时手工推导也是可行的。
 
        动态测试
        动态测试指通过运行程序发现错误,分为黑盒测试法、白盒测试法和灰盒测试法等。
        (1)黑盒法。把被测试对象看成一个黑盒子,测试人员完全不考虑程序的内部结构和处理过程,只在软件的接口处进行测试,依据需求规格说明书,检查程序是否满足功能要求。因此,黑盒测试又称为功能测试或数据驱动测试,使用这种方法,为了做到穷尽测试,至少必须对所有输入数据的各种可能值的排列组合都进行测试。黑盒测试使用所有有效和无效的输入数据来测试程序是不现实的,所以黑盒测试同样不能做到穷尽测试,只能选取少量最有代表性的输入数据,以期用较少的代价暴露出较多的程序错误。常用的黑盒测试用例的设计方法有等价类划分、边界值分析、错误推测和因果图等。
        等价类划分把程序的输入域划分成若干部分,然后从每个部分中选取少数有代表性的数据作为测试用例,每一类代表性数据在测试中的作用等价于这一类中的其他值。划分等价类时,首先要把数目极多的输入分成若干个等价类。所谓等价类就是某个输入域的集合,对于一个等价类中的输入值来说,它们揭示程序中错误的作用是等效的。
        边界值分析是一种补充等价类划分的测试用例设计技术,它不选择等价类的任意元素,而选择等价类边界的测试用例。实践证明,为检验边界附近的处理而专门设计测试用例,常常可以取得良好的测试效果。
        错误推测法基于经验和直觉推测程序中所有可能存在的各种错误,有针对性地设计测试用例的方法。基本思想是列举出程序中所有可能的错误和容易发生错误的特殊情况,再根据它们选择测试用例。
        因果图法从自然语言书写的程序规格说明的描述中找出因(输入条件)和果(输出或程序状态的改变),通过因果图转换为判定表。
        (2)白盒法。把测试对象看做一个打开的盒子,测试人员必须了解程序的内部结构和处理过程,以检查处理过程的细节为基础,对程序中尽可能多的逻辑路径进行测试,检验内部控制结构和数据结构是否有错,实际的运行状态与预期的状态是否一致。由于白盒测试是结构测试,所以被测对象基本上是源程序,以程序的内部逻辑为基础设计测试用例。常用的白盒测试用例设计方法有基本路径测试、循环覆盖测试及逻辑覆盖测试等。
        逻辑覆盖是以程序内部逻辑为基础的测试技术,常用的有语句覆盖、判定覆盖、条件覆盖、条件判定覆盖、修正的条件判断覆盖、条件组合覆盖、点覆盖、边覆盖和路径覆盖等。
        循环覆盖是指覆盖程序中所有的循环,包括单循环及嵌套循环。
        基本路径法在程序控制流程图的基础上,通过分析控制结构的环路复杂性导出基本路径集合,然后设计测试用例,保证这些路径都至少通过一次。
        (3)灰盒法。灰盒测试是一种介于白盒测试与黑盒测试之间的测试,它关注输出对于输入的正确性,同时也关注内部表现,但这种关注不像白盒测试那样详细且完整,而只是通过一些表征性的现象、事件及标志来判断程序内部的运行状态。
        灰盒测试结合了白盒测试和黑盒测试的要素,考虑了用户端、特定的系统知识和操作环境,在系统组件的协同性环境中评价应用软件的设计。
 
        静态测试
        静态测试是指被测试程序不在机器上运行,而采用人工检测和计算机辅助静态分析的手段对程序进行检测。静态分析中进行人工测试的主要方法有桌前检查(Desk Checking)、代码审查和代码走查。经验表明,使用这种方法能够有效地发现30%~70%的逻辑设计和编码错误。
               桌前检查
               由程序员自己检查自己编写的程序。程序员在程序通过编译之后,进行单元测试设计之前,对源程序代码进行分析、检验,并补充相关的文档,目的是发现程序中的错误。检查项目如下所述。
               .检查变量的交叉引用表。重点是检查未说明的变量和违反了类型规定的变量;还要对照源程序,逐个检查变量的引用、变量的使用序列,临时变量在某条路径上的重写情况,局部变量、全局变量与特权变量的使用等。
               .检查标号的交叉引用表。验证所有标号的正确性;检查所有标号的命名是否正确,以及转向指定位置的标号是否正确。
               .检查子程序、宏、函数。验证每次调用与被调用位置是否正确;确认每次被调用的子程序、宏和函数是否存在;检验调用序列中调用方式与参数顺序、个数和类型上的一致性。
               .等值性检查。检查全部等价变量的类型的一致性,解释所包含的类型差异。
               .常量检查。确认每个常量的取值和数制、数据类型;检查常量每次引用同它的取值、数制和类型的一致性。
               .标准检查。用标准检查程序或手工检查程序中违反标准的问题。
               .风格检查。检查在程序设计风格方面发现的问题。
               .比较控制流。比较由程序员设计的控制流图和由实际程序生成的控制流图,寻找和解释每个差异,修改文档并校正错误。
               .选择、激活路径。在程序员设计的控制流图中选择路径,再到实际的控制流图中激活这条路径。如果选择的路径在实际控制流图中不能激活,则源程序可能有错。用这种方法激活的路径集合应保证源程序模块的每行代码都被检查,即桌前检查应至少是语句覆盖的。
               .对照程序的规格说明,详细阅读源代码。程序员对照程序的规格说明书、规定的算法和程序设计语言的语法规则,仔细地阅读源代码,逐字逐句进行分析和思考,比较实际的代码和期望的代码,并从它们的差异中发现程序的问题和错误。
               .补充文档。桌前检查的文档是一种过渡性的文档,不是公开的正式文档。通过编写文档,也是对程序的一种下意识的检查和测试,可以帮助程序员发现和抓住更多的错误。
               由于程序员熟悉自己的程序和自身的程序设计风格,这种桌前检查可以节省很多的检查时间,但应避免主观片面性。
               代码审查
               代码审查是由若干程序员和测试员组成一个会审小组,通过阅读、讨论和争议,对程序进行静态分析的过程。代码审查分两步。
               第一步,小组负责人提前把设计规格说明书、控制流程图、程序文本及有关要求、规范等分发给小组成员,作为评审的依据。小组成员在充分阅读这些材料之后,进入审查的第二步。
               第二步,召开程序审查会。在会上,首先由程序员逐句讲解程序的逻辑。在此过程中,程序员或其他小组成员可以提出问题,展开讨论,审查错误是否存在。实践表明,程序员在讲解过程中能发现许多原来自己没有发现的错误,而讨论和争议则促进了问题的暴露。
               在会前,应当给会审小组每个成员准备一份常见错误的清单,把以往所有可能发生的常见错误罗列出来,供与会者对照检查,以提高会审的实效。这个常见错误清单也叫做检查表,它把程序中可能发生的各种错误进行分类,对每一类列举出尽可能多的典型错误,然后把它们制成表格,供在会审时使用。这种检查表类似于本章单元测试中给出的检查表。
               代码走查
               代码走查与代码审查基本相同,其过程也分为两步。
               第一步,把材料先发给走查小组每个成员,让他们认真研究程序,然后再开会。
               第二步,开会的程序与代码会审不同,不是简单地读程序和对照错误检查表进行检查,而是让与会者“充当”计算机。即首先由测试组成员为被测程序准备一批有代表性的测试用例,提交给走查小组。走查小组开会,集体扮演计算机角色,让测试用例沿程序的逻辑运行一遍,随时记录程序的踪迹,供分析和讨论使用。
               值得说明的是,使用静态测试的方法也可以实现白盒测试。例如,使用人工检查代码的方法来检查代码的逻辑问题也属于白盒测试的范畴。
 
        静态测试和动态测试
               静态测试
               静态测试方法包括检查单和静态分析方法,对软件文档的静态测试方法主要是以检查单的形式进行文档审查,而对软件代码的静态测试方法一般采用代码审查、代码走查和静态分析的形式进行。
               静态分析是一种对代码的机械性和程序化的特性分析方法。一般包括控制流分析、数据流分析、接口分析和表达式分析。
               代码审查是检查代码和设计的一致性、代码执行标准的情况、代码逻辑表达的正确性、代码结构的合理性以及代码的可读性。代码审查应根据所使用的语言和编码规范确定审查所用的检查单,检查单的设计或采用应经过评审。
               代码走查是由测试人员组成小组,准备一批有代表性的测试用例,集体扮演计算机的角色,按照程序的逻辑,逐步运行测试用例,查找被测软件缺陷。代码走查应由测试人员集体阅读讨论程序,是用“人脑”执行测试用例并检查程序。
               对于规模较小、安全性要求很高的代码也可进行形式化证明。静态分析常需要使用软件工具进行。
               静态测试的特点有:不必设计在计算机上执行的测试用例;可充分发挥人的逻辑思维优势;不需特别条件,容易开展;发现错误的同时也就定位了错误,不需作额外的错误定位工作。
               动态测试
               动态测试是建立在程序的执行过程中,根据是否对被测对象内部的了解,分为黑盒测试和白盒测试。
               黑盒测试是一种按照软件功能说明设计测试数据的技术,不考虑程序内部结构和编码结构,也不需考虑程序的语句及路径,只需了解输入/输出之间的关系,依靠这一关系和软件功能说明确定测试数据,判定测试结果的正确性。黑盒测试又称功能测试、数据驱动测试或基于需求的测试。
               白盒测试是一种按照程序内部逻辑结构和编码结构设计测试数据的技术,可以看到程序内部结构,并根据内部结构设计测试数据,使程序中的每个语句、每个条件分支、每个控制路径的覆盖情况都在测试中受到检验。白盒测试又称结构测试、逻辑测试或基于程序的测试。
               动态测试的特点有:实际运行被测程序;必须设计测试用例来运行;测试结果分析工作量大,测试工作费时、费力;投入人员多、设备多,处理数据多,要求有较好的管理和工作规程。
               在软件动态测试过程中,应采用适当的测试方法,实现测试要求。配置项测试和系统测试一般采用黑盒测试方法;部件测试一般主要采用黑盒测试方法,辅助以白盒测试方法;单元测试一般采用白盒测试方法,辅助以黑盒测试方法。



更多复习资料
请登录电脑版软考在线 www.rkpass.cn

京B2-20210865 | 京ICP备2020040059号-5
京公网安备 11010502032051号 | 营业执照
 Copyright ©2000-2023 All Rights Reserved
软考在线版权所有