1、第七章、 实现,编码和测试统称为实现 编码 测试,2,7.1 编码,所谓编码就是把软件设计的结果翻译成计算机可以“理解”的形式用某种程序设计语言书写的程序。 程序设计语言的特性和编码途径会对程序的可靠性、可读性、可测试性和可维护性产生深远的影响。,3,7.1 编码,汇编语言的语句和计算机硬件操作有一一对应关系。 高级语言使用的概念和符号与人们通常使用的概念和符号比较接近,它的一个语句往往对应若干条机器指令。 程序员在相同时间可以写出的汇编语言语句和高级语言语句数目是相同的,因此在选择语言时,采用高级语言的效率要比汇编语言高。 汇编语言适用范围 对程序执行时间和利用的空间有很严格限制的情况。 需
2、要产生任意的甚至非法的指令序列。 体系结构特殊的处理机,在这类机器上不能实现高级语言编译程序。 大型系统中执行时间非常关键的(或直接依赖于硬件的)一小部分代码。,4,高级语言的理想标准,选用的高级语言应该有理想的模块化机制,以及可读性好的控制结构和数据结构。 语言特点应该使编译程序能够尽可能多地发现程序中的错误。 选用的语言应该有良好的独立编译机制。 选择高级语言的实用标准 系统用户的要求 如果所开发的系统由用户负责维护,用户通常要求用他们熟悉的语言书写程序。 可以使用的编译程序 运行目标系统的环境中可以提供的编译程序往往限制了可以选用的语言的范围。 可以得到的软件工具 如果某种语言有支持程序
3、开发的软件工具可以利用,则目标系统的实现和验证都变得比较容易。,5,工程规模 如果工程规模很庞大,现有的语言又不完全适用,那么设计并实现一种供这个工程项目专用的程序设计语言,可能是一个正确的选择。 程序员的知识 如果和其他标准不矛盾,那么应该选择一种已经为程序员所熟悉的语言。 软件可移植性要求 如果目标系统将在几台不同的计算机上运行,或者预期的使用寿命很长,那么选择一种标准化程度高、程序可移植性好的语言就是很重要的。 软件的应用领域 所谓的通用程序设计语言实际上并不是对所有应用领域都同样适用,因此,选择语言时应该充分考虑目标系统的应用范围。,6,7.1 .2 编码风格,源程序代码的逻辑简明清晰
4、、易读易懂是好程序的一个重要标准,为了做到这一点,应该遵循下述规则: 程序内部的文档(恰当的标识符、适当的注解和程序的视觉组织等 ) 恰当的标识符 选取含义鲜明的名字,使它能正确地提示程序对象所代表的实体。 如果使用缩写,那么缩写规则应该一致,并且应该给每个名字加注解。,7,一般的命名约定,有不少人编程时用拼音给函数或变量命名,这样做并不能说明你很爱国,却会让用此程序的人迷糊(很多南方人不懂拼音)。程序中的英文一般不会太复杂,用词要力求准确。 匈牙利命名法是Microsoft公司倡导的 Maguire 1993,虽然很烦琐,但用习惯了也就成了自然。没有人强迫你采用何种命名法,但有一点应该做到:
5、自己的程序命名必须一致。(1)宏定义用大写字母加下划线表示,如MAX_LENGTH; (2)函数用大写字母开头的单词组合而成,如SetName, GetName ; (3)指针变量加前缀p,如 *pNode ; (4)BOOL 变量加前缀b,如 bFlag ; (5)int 变量加前缀i,如 iWidth ; (6)float 变量加前缀f,如 fWidth ; (7)double变量加前缀d,如 dWidth ; (8)字符串变量加前缀str,如 strName ; (9)枚举变量加前缀e,如 eDrawMode ; (10)类的成员变量加前缀m_,如 m_strName, m_iWidth
6、 ;,8,适当的注解 通常在每个模块开始处有一段序言性的注解,简要描述模块的功能、主要算法、接口特点、重要数据以及开发简史。 插在程序中间与一段程序代码有关的注解,主要解释包含这段代码的必要性。 对于用高级语言书写的源程序,不需要用注解的形式把每个语句翻译成自然语言,应该利用注解提供一些额外的信息。 应该用空格或空行清楚地区分注解和程序。 程序的视觉组织 程序清单的布局对于程序的可读性也有很大影响,应该利用适当的阶梯形式使程序的层次结构清晰明显。,7.1 .2 编码风格,9,7.1 .2 编码风格,数据说明 数据说明的次序应该标准化(例如,按照数据结构或数据类型确定说明的次序)。 当多个变量名
7、在一个语句中说明时,应该按字母顺序排列这些变量。 如果设计时使用了一个复杂的数据结构,则应该用注解说明用程序设计语言实现这个数据结构的方法和特点。,10,7.1 .2 编码风格,语句构造 构造语句时应该遵循的原则是,每个语句都应该简单而直接,不能为了提高效率而使程序变得过分复杂。 不要为了节省空间而把多个语句写在同一行; 尽量避免复杂的条件测试; 尽量减少对“非”条件的测试; 避免大量使用循环嵌套和条件嵌套; 利用括号使逻辑表达式或算术表达式的运算次序清晰直观。,11,7.1 .2 编码风格,输入输出 对所有输入数据都进行检验; 检查输入项重要组合的合法性; 保持输入格式简单; 使用数据结束标
8、记,不要要求用户指定数据的数目; 明确提示交互式输入的请求,详细说明可用的选择或边界数值; 当程序设计语言对格式有严格要求时,应保持输入格式一致; 设计良好的输出报表; 给所有输出数据加标志。,12,7.1 .2 编码风格,效率 效率主要指处理机时间和存储器容量两个方面。 在讨论提高效率前,应该记住3条原则: 效率是性能要求,因此应该在需求分析阶段确定效率方面的要求。 效率是靠好设计来提高的。 程序的效率和程序的简单程度是一致的。不要牺牲程序的清晰性和可读性来不必要地提高效率。 从三个方面讨论程序效率的问题。,13,程序运行时间 写程序之前先简化算术的和逻辑的表达式; 仔细研究嵌套的循环,以确
9、定是否有语句可以从内层往外移; 尽量避免使用多维数组; 尽量避免使用指针和复杂的表; 使用执行时间短的算术运算; 不要混合使用不同的数据类型; 尽量使用整数运算和布尔表达式。 在效率是决定性因素的应用领域,尽量使用有良好优化特性的编译程序,以自动生成高效目标代码。,7.1 .2 编码风格,14,存储器效率 在大型计算机中必须考虑操作系统页式调度的特点,一般说来,使用能保持功能域的结构化控制结构,是提高效率的好方法。 在微处理机中如果要求使用最少的存储单元,则应选用有紧缩存储器特性的编译程序,在非常必要时可以使用汇编语言。 提高执行效率的技术通常也能提高存储器效率。提高存储器效率的关键同样是简单
10、。,7.1 .2 编码风格,15,3、输入输出效率 如果用户为了给计算机提供输入信息或为了理解计算机输出的信息,所需花费的脑力劳动是经济的,那么人和计算机通信的效率就高。因此,简单清晰同样是提高人机通信效率的关键。 硬件之间的通信效率比较复杂,但从写程序的角度看,有些简单原则可以提高输入输出效率: 所有输入输出都应该有缓存,以减少用于通信的额外开销。 对二级存储器应该选用最简单的访问方法。 二级缓存器的输入输出应该以信息组为单位进行。 如果“超高效的”输入输出很难被人理解,则不采用这种方法。,7.1 .2 编码风格,16,7.2 软件测试基础,测试的目的就是在软件投入生产运行之前,尽可能多地发
11、现软件中的错误。 目前软件测试仍然是保证软件质量的关键步骤。软件测试在软件生命周期中横跨两个阶段。 通常在编写出每个模块之后就对它做必要的测试(称为单元测试),模块的编写者和测试者是同一个人,编码和单元测试属于软件生命周期的同一个阶段。 对软件系统进行各种综合测试,通常由专门的测试人员承担这项工作。,17,7.2.1 软件测试的目标,测试的正确定义是“为了发现程序中的错误而执行程序的过程”。 测试只能查找出程序中的错误,不能证明程序中没有错误。 一般测试工作通常由非编码人员组成的小组来测试完成。,18,7.2.2 软件测试准则,所有测试都应该能追溯到用户需求 应该在测试前制定测试计划 把Par
12、eto原理应用到软件测试中 应该从“小规模”开始测试,逐步进行大规模测试。 穷举测试是不可能的 为了达到最佳的测试效果,应该由独立的第三方从事测试工作。,19,7.2.3 测试方法,黑盒测试法(功能测试)把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。 黑盒测试是在程序接口进行的测试,它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据产生正确的输出信息,并且保持外部信息的完整性。,20,7.2.3 测试方法,白盒测试法(结构测试)把程序看成装在一个透明的白盒子里,也就是完全了解程序的结构和处理过程。这种方法按照程序内部的逻辑测试程序,检验程序中的每条通路
13、是否都能按预定要求正确工作。,21,7.2.4软件测试的步骤,模块测试模块测试的目的是保证每个模块作为一个单元能正确运行,所以模块测试通常又称为单元测试。在这个测试步骤中所发现的往往是编码和详细设计的错误。,22,7.2.4 软件测试的步骤,子系统测试子系统测试是把经过单元测试的模块放在一起形成一个子系统来测试。模块相互间的协调和通信是这个测试过程中的主要问题,因此这个步骤着重测试模块的接口。,23,7.2.4 软件测试的步骤,系统测试系统测试是把经过测试的子系统装配成一个完整的系统来测试。在这个测试步骤中发现的往往是软件设计中的错误,也可能发现需求说明中的错误。 不论是子系统测试还是系统测试
14、,都兼有检测和组装两重含义,通常称为集成测试。,24,7.2.4 软件测试的步骤,验收测试验收测试的目的是验证系统确实能够满足用户的需要,在这个测试步骤中发现的往往是系统需求说明书中的错误。,25,7.2.4 软件测试的步骤,平行运行 所谓平行运行就是同时运行新开发出来的系统和将被它取代的旧系统,以便比较新旧两个系统的处理结果。 可以在准生产环境中运行新系统而又不冒风险; 用户能有一段熟悉新系统的时间; 可以验证用户指南和使用手册之类的文档; 能够以准生产模式对新系统进行全负荷测试,可以用测试结果验证性能指标。,26,7.2.5测试阶段的信息流,27,7.2.5 测试阶段的信息流,测试阶段的信
15、息流。输入信息有两类: 软件配置,包括需求说明书,设计说明书和源程序清单等; 测试配置,包括测试计划和测试方案。 所谓测试方案不仅仅是测试时使用的输入数据(称为测试用例),还应该包括每组输入数据预定要检验的功能,以及每组输入数据预期应该得到的正确输出。,28,7.3 单元测试,单元测试可以使用白盒测试法,而且对多个模块的测试可以并行地进行。在单元测试期间主要评价模块的下述五个特性。,29,1. 模块接口,首先应该对通过模块接口的数据流进行测试。在对接口进行测试时主要检查下述各点: 参数和(由调用模块送来的)变元的数目是否相等? 参数和(由调用模块送来的)变元的属性是否匹配? 参数和(由调用模块
16、送来的)变元的单位系统是否匹配? 是否修改了只做输入用的变元? 全程变量的定义和用法在各个模块中是否一致?,30,2. 局部数据结构,对于一个模块而言,局部数据结构是常见的错误来源。应该仔细设计测试方案,以便发现下述类型的错误: 错误的或不相容的说明; 使用尚未赋值或尚未初始化的变量; 错误的初始值或不正确的缺省值; 错误的变量名字(拼写错或截短了); 数据类型不相容; 上溢、下溢或地址异常。,31,3. 重要的执行通路,应该设计测试方案用来发现由于错误的计算、不正确的比较或不适当的控制流而造成的错误。 通常不可能进行群尽测试,因此,在单元测试期间选择最有代表性、最可能发现错误的执行通路进行测
17、试就是十分关键的。,32,3. 重要的执行通路,一些常见的错误: 比较数据类型不同的量; 逻辑运算符不正确或优先次序的错误; 当由于精度问题两个量不会相等时,程序中却期待着相等条件的出现; “差1”错(即,多循环一次或少循环一次); 错误的或不存在的循环终止条件; 当遇到发散的迭代时不能终止循环; 错误地修改循环变量。,33,4. 出错处理通路,当评价出错处理通路时,应该着重测试下述一些可能发生的错误: 对错误的描述是难于理解的; 记下的错误与实际遇到的错误不同; 在对错误进行处理之前,错误条件已经引起系统干预; 对错误的处理不正确; 描述错误的信息不足以帮助确定造成错误的位置。,34,5.
18、边界条件,边界测试是单元测试中最后的也可能是最重要的任务。 软件常常在它的边界上失效,例如,处理n元数组的第n个元素时,或做到i次循环中的第i次重复时,往往会发生错误。 使用刚好小于、刚好等于和刚好大于最大值或最小值的数据结构、控制量和数据值的测试方案,非常可能发现软件中的错误。,35,7.3.2 代码审查,人工测试源程序可以由编写者本人非正式地进行,也可以由审查小组正式进行。后者称为代码审查,它是一种非常有效的程序验证技术,对于典型的程序来说,可以查出30%70%的逻辑设计错误和编码错误。 审查小组最好由下述四人组成: 组长,他应该是一个很有能力的程序员,而且没有直接参与这项工程。 程序的设
19、计者。 程序的编写者。 程序的测试者。,36,7.3.3 计算机测试,模块并不是一个独立的程序,因此必须为每个单元测试开发驱动软件和(或)存根软件。 通常驱动程序也就是一个“主程序”,它接收测试数据,把这些数据传送给被测试的模块,并且印出有关的结果。 存根程序代替被测试的模块所调用的模块。因此存根程序也可以称为“虚拟子程序”。它使用被它代替的模块的接口,可能做最少量的数据操作,印出对入口的检验或操作结果,并且把控制归还给调用它的模块。,37,7.4 集成测试,集成测试是组装软件的系统技术,主要目标是发现与接口有关的问题。例如: 数据穿过接口时可能丢失; 一个模块对另一个模块可能有由于疏忽而造成
20、的有害影响; 把子功能组合起来可能不产生预期的主功能; 个别看来是可以接受的误差可能积累到不能接受的程度; 全程数据结构可能有问题等等。,38,模块组装成程序的方法,非渐增式测试方法: 是先分别测试每个模块,再把所有模块按设计要求放在一起结合成所要的程序。 渐增式测试方法: 是把下一个要测试的模块同已经测试好的那些模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试。这种方法实际上同时完成单元测试和集成测试。,39,这两种方法哪种更好一些呢?,因为非渐增式测试方法分别测试每个模块,需要编写的测试软件通常比较多,所以需要的工作量比较大;渐增式的测试方法利用已测试过的模块作为部分测
21、试软件,因此开销比较小。 渐增式测试可以较早发现模块间的接口错误;非渐增式测试最后才把模块组装在一起,因此接口错误发现得晚。,40,这两种方法哪种更好一些呢?,非渐增式测试一下子把所有模块组合在一起,如果发现错误则较难诊断定位;反之,使用渐增式测试方法时,如果发生错误则往往和最近加进来的那个模块有关。 渐增式测试方法把已经测试好的模块和新加进来的那个模块一起测试,已测试好的模块可以在新的条件下受到新的检验,因此,这种方法对程序的测试更彻底。,41,这两种方法哪种更好一些呢?,渐增式测试需要较多的机器时间,这是因为测试每个模块时所有已经测试完的模块也都要跟着一起运行,当程序规模较大时增加的机器时
22、间是相当明显的。 使用非渐增式测试方法可以并行测试所有模块,因此能充分利用人力,加快工程进度。 总的说来,渐增式测试方法比较好。,42,7.4.1 自顶向下结合,自顶向下的结合方法是一个日益为人们广泛采用的组装软件的途径。 从主控制模块(“主程序”)开始,沿着软件的控制层次向下移动,从而逐渐把各个模块结合起来。 在把附属于(以及最终附属于)主控制模块的那些模块组装到软件结构中去时,或者使用深度优先的策略,或者使用宽度优先的策略。,43,自顶向下结合,44,自顶向下结合,深度优先的结合方法: 先组装在软件结构的一条主控制通路上的所有模块。 选择一条主控制通路取决于应用的特点,并且有很大任意性。
23、宽度优先的结合方法: 是沿软件结构水平地移动,把处于同一个控制层次上的所有模块组装起来。,45,把模块结合进软件结构的具体步骤,对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块; 根据选定的结合策略(深度优先或宽度优先),每次用一个实际模块代换一个存根程序(新结合进来的模块往往又需要新的存根程序); 在结合进一个模块的同时进行测试; 为了保证加入模块没有引进新的错误,可能需要进行回归测试(即,全部或部分地重复以前做过的测试)。,46,自顶向下结合方法的优点,自顶向下的结合策略能够在测试的早期对主要的控制或关键的抉择进行检验。早期认识到这类问题是很有好处的。 如果选择深度
24、优先的结合方法,可以在早期实现软件的一个完整的功能并且验证这个功能。可以增强开发人员和用户双方的信心。,47,自底向上结合,自底向上测试从“原子”模块(即在软件结构最低层的模块)开始组装和测试。 因为是从底部向上结合模块,总能得到需要的下层模块处理功能,所以不需要存根程序。,48,自底向上结合策略的实现步骤,把低层模块组合成实现某个特定的软件子功能的族; 写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出; 对由模块组成的子功能族进行测试; 去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的子功能族。,49,自底向上结合 示意图,50,不同集成测试策略的比较,自顶向
25、下测试方法 主要优点是不需要测试驱动程序,能够在测试阶段的早期实现并验证系统的主要功能,而且能在早期发现上层模块的接口错误。 自顶向下测试方法的主要缺点是需要存根程序,可能遇到与此相联系的测试困难,低层关键模块中的错误发现较晚,而且用这种方法在早期不能充分展开人力。 自底向上测试方法 优缺点与上述自顶向下测试方法的优缺点刚好相反。 一般说来,纯粹自顶向下或纯粹自底向上的策略可能都不实用。,51,混合策略,改进的自顶向下测试方法 基本上使用自顶向下的测试方法,但是在早期,就使用自底向上的方法测试软件中的少数关键模块。 它的缺点是测试关键模块时需要驱动程序。 混合法 对软件结构中较上层,使用的是自
26、顶向下方法;对软件结构中较下层,使用的是自底向上方法,两者相结合。,52,回归测试,在集成测试的范畴中,所谓回归测试是指重新执行已经做过的测试的某个子集,以保证一些程序的变化没有带来非预期的副作用。 回归测试集包括3类不同的测试用例: 检测软件全部功能的代表性测试用例子; 专门针对可能受修改影响的软件功能的附加测试; 针对被修改过的软件成分的测试。,53,7.5 确认测试,确认测试也称为验收测试,它的目标是验证软件的有效性。 软件有效性的一个简单定义是: 如果软件的功能和性能如同用户所合理地期待的那样,则软件是有效的。 在需求分析阶段产生的文档准确地描述了用户对软件的合理期望,因此是软件有效的
27、标准,也是验收测试的基础。,54,7.5.1 确认测试的范围,验收测试的目的是向未来的用户表明系统能够像预定要求那样工作。 验收测试的范围与系统测试类似,但是也有一些差别,例如: 某些已经测试过的纯粹技术性的特点可能不需要再次测试; 对用户特别感兴趣的功能或性能,可能需要增加一些测试; 通常主要使用生产中的实际数据进行测试; 可能需要设计并执行一些与用户使用步骤有关的测试。,55,7.5.1 验收测试的范围,验收测试必须有用户积极参与,或者以用户为主进行。 验收测试一般使用黑盒测试法。 测试计划包括要进行的测试的种类和进度安排。 测试过程规定了用来检测软件是否与需求一致的测试方案。 通过测试和
28、调试要保证软件能满足所有功能要求,能达到每个性能要求,文档资料是准确而完整的。 还应保证软件能满足其他预定的要求(安全性、兼容性等),56,7.5.2 软件配置复查,复查软件配置的目的是保证软件配置的所有成分都齐全,各方面的质量都符合要求,文档与程序一致,具有维护阶段所必需的细节,而且已经编排好目录。 在验收测试的过程中应该严格遵循用户指南以及其他操作程序,以便检验这些使用手册的完整性和正确性。必须仔细记录发现的遗漏或错误,并且适当地补充和改正。,57,7.5.3 Alpha 和Beta测试,验收测试是由最终用户而不是系统开发者进行的,这个过程根据不同的软件,需要经历不同的过程,对为许多用户开
29、发的软件,需要经历Alpha,Beta测试。 Alpha测试:由用户在开发者的场所进行,并且在开发者对用户的“指导”下测试。是在“受控”的环境下进行的测试。 Beta测试:由软件的最终用户们在一个或多个客户场所进行。,58,7.6 白盒测试技术,设计测试方案是测试阶段的关键技术问题。 所谓测试方案包括预定要测试的功能,应该输入的测试数据和预期的结果(测试用例) 。其中最困难的问题是设计测试用的输入数据。 不同的测试数据发现程序错误的能力差别很大,为了提高测试效率降低测试成本,应该选用高效的测试数据。,59,设计测试方案的基本目标,设计测试方案的基本目标是,确定一组最可能发现某个错误或某类错误的
30、测试数据。 已经研究出许多设计测试数据的技术,这些技术各有优缺点,没有哪一种是最好的,更没有哪一种可以代替其余所有技术;同一种技术在不同的应用场合效果可能相差很大。 因此,通常需要联合使用多种设计测试数据的技术。,60,测试方案的设计技术,设计技术主要有: 适用于白盒测试的逻辑覆盖法; 适用于黑盒测试的等价划分、边界值分析以及错误推测法等。 通常的做法是,用黑盒法设计基本的测试方案,再用白盒法补充一些方案。 本节主要介绍用白盒方法测试软件时设计测试数据的典型技术。,61,7.6.1 逻辑覆盖,有选择地执行程序中某些最有代表性的通路是对穷尽测试的唯一可行的替代办法。 所谓逻辑覆盖是对一系列测试过
31、程的总称,这组测试过程逐渐进行越来越完整的通路测试。 测试数据执行程序逻辑的程度可以划分成不同的等级,从覆盖源程序语句的详尽程度分析,大致有以下一些不同的覆盖标准。,62,逻辑覆盖,语句覆盖 判定覆盖 条件覆盖 判定条件覆盖 条件组合覆盖 点覆盖 边覆盖 路径覆盖,63,语句覆盖,为了暴露程序中的错误,至少每个语句应该执行一次。 语句覆盖的含义是,选择足够多的测试数据,使被测程序中每个语句至少执行一次。,64,语句覆盖,65,语句覆盖,上图是一个被测模块的流程图。 为了使每个语句都执行一次,程序的执行路径应该是sacbed,为此只需要输入下面的测试数据(实际上X可以是任意实数): A=2,B=
32、0,X=4,66,语句覆盖缺点,这组数据只测试了判定条件为真的情况,如果条件为假时处理有错误,显然不能发现。 语句覆盖是很弱的逻辑覆盖标准,语句覆盖对程序的逻辑覆盖很少。 语句覆盖只关心判定表达式的值,而没有分别测试判定表达式中每个条件取不同值时的情况。,67,判定覆盖,判定覆盖又叫分支覆盖。 它的含义是: 不仅每个语句必须至少执行一次, 而且每个判定的每种可能的结果都应该至少执行一次,也就是每个判定的每个分支都至少执行一次。,68,判定覆盖,对于上述例子来说,能够分别覆盖路径sacbed和sabd的两组测试数据,或者可以分别覆盖路径sacbd和sabed的两组测试数据,都满足判定覆盖标准。
33、例如,用下面两组测试数据就可做到判定覆盖: A=3,B=0,X=3(覆盖sacbd) A=2,B=1,X=1(覆盖sabed) 判定覆盖比语句覆盖强,但是对程序逻辑的覆盖程度仍然不高,例如,上面的测试数据只覆盖了程序全部路径的一半。,69,条件覆盖,条件覆盖的含义是: 不仅每个语句至少执行一次, 而且使判定表达式中的每个条件都取到各种可能的结果。,70,条件覆盖,例子中共有两个判定表达式,每个表达式中有两个条件,为了做到条件覆盖,应该选取测试数据使得: 在a点有下述各种结果出现: A1,A1,B=0,B0 在b点有下述各种结果出现: A=2,A2,X1,X1,71,条件覆盖,只需要使用下面两组
34、测试数据就可以达到上述覆盖标准: A=2,B=0,X=4 (满足A1,B=0,A=2和X1的条件,执行路径sacbed) A=1,B=1,X=1 (满足A1,B0,A2和X1的条件,执行路径sabd),72,条件覆盖,条件覆盖通常比判定覆盖强。 因为它使判定表达式中每个条件都取到了两个不同的结果,判定覆盖却只关心整个判定表达式的值。 例如,上面两组测试数据也同时满足判定覆盖标准。 但是,也可能有相反的情况。 虽然每个条件都取到了两个不同的结果,判定表达式却始终只取一个值。 A=2,B=0,X=1(执行路径:sacbed) A=1,B=1,X=2 (执行路径:sabed),73,判定条件覆盖,既
35、然判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖,自然会提出一种能同时满足这两种覆盖标准的逻辑覆盖,这就是判定条件覆盖。 它的含义是,选取足够多的测试数据,使得判定表达式中的每个条件都取到各种可能的值,而且每个判定表达式也都取到各种可能的结果。,74,判定条件覆盖,对于例子而言,下述两组测试数据满足判定条件覆盖标准: A=2,B=0,X=4 A=1,B=1,X=1 但是,这两组测试数据也就是为了满足条件覆盖标准最初选取的两组数据,因此,有时判定条件覆盖也并不比条件覆盖更强。,75,条件组合覆盖,条件组合覆盖是更强的逻辑覆盖标准。 它要求选取足够多的测试数据,使得每个判定表达式中条件的
36、各种可能组合都至少出现一次。,76,条件组合覆盖,对于例子,共有八种可能的条件组合,它们是: 第一个判定表达式:A1 AND B=0 A1,B=0 ;表达式真 A1,B0 ;表达式假 A1,B=0 ;表达式假 A1,B0 ;表达式假,77,条件组合覆盖,第二个判定表达式:A=2 OR X1 A=2,X1 ;表达式真 A=2,X1 ;表达式真 A2,X1 ;表达式真 A2,X1 ;表达式假 注意:这时的X值是在第二个判定框(b点)的X值,78,条件组合覆盖,下面的四组测试数据可以使上面列出的八种条件组合每种至少出现一次: A=2,B=0,X=4 /在第二个判定框(b点)的X=2(针对1,5两种组
37、合,执行路径sacbed) A=2,B=1,X=1 /在第二个判定框(b点)的X=1(针对2,6两种组合,执行路径sabed) A=1,B=0,X=2 /在第二个判定框(b点)的X=2(针对3,7两种组合,执行路径sabed) A=1,B=1,X=1 /在第二个判定框(b点)的X=1(针对4,8两种组合,执行路径sabd),79,条件组合覆盖,显然,满足条件组合覆盖标准的测试数据,也一定满足判定覆盖、条件覆盖和判定条件覆盖标准。 条件组合覆盖是前述几种覆盖标准中最强的。 但是,满足条件组合覆盖标准的测试数据并不一定能使程序中的每条路径都执行到。 例如,上述四组测试数据都没有测试到路径sacbd
38、。,80,点覆盖,图论中点覆盖的概念定义如下:如果连通图G的子图G是连通的,而且包含G的所有节点,则称G是G的点覆盖。 满足点覆盖标准要求选取足够多的测试数据,使得程序执行路径至少经过程序图中每个节点一次。显然,点覆盖标准和语句覆盖标准是相同的。,81,边覆盖,图论中边覆盖的定义是:如果连通图G的子图G”是连通的,而且包含G的所有边,则称G”是G的边覆盖。 为了满足边覆盖的测试标准,要求选取足够多测试数据,使得程序执行路径至少经过程序图中每条边一次。 通常边覆盖和判定覆盖是一致的。,82,路径覆盖,路径覆盖的含义是:选取足够多测试数据,使程序的每条可能路径都至少执行一次(如果程序图中有环,则要
39、求每个环至少经过一次)。,83,路径覆盖,例子中共有四条可能的执行路径, 它们是:1-2-3;1-2-6-7;1-4-5-3和1-4-5-6-7。 因此,对于这个例子而言,为了做到路径覆盖必须设计四组测试数据。 例如,下面四组测试数据可以满足路径覆盖的要求: A=1,B=1,X=1 (执行路径 1-2-3) A=1,B=1,X=2 (执行路径 1-2-6-7) A=3,B=0,X=1 (执行路径 1-4-5-3) A=2,B=0,X=4 (执行路径 1-4-5-6-7),84,路径覆盖,路径覆盖是相当强的逻辑覆盖标准,它保证程序中每条可能的路径都至少执行一次,因此这样的测试数据更有代表性,暴露
40、错误的能力也比较强。 但是,为了做到路径覆盖只需考虑每个判定表达式的取值,并没有检验表达式中条件的各种可能组合情况。 如果把路径覆盖和条件组合覆盖结合起来,可以设计出检错能力更强的测试数据。,85,逻辑覆盖小节,86,7.6.2 控制结构测试,现有的很多白盒测试技术,是根据程序的控制结构设计测试的技术,常用的控制结构测试技术有: 基本路径测试 条件测试 循环测试,87,1、基本路经测试,基本路径测试,首先计算程序的环形复杂度,并用该复杂度为指南定义执行路径的基本集合,从该基本集合导出的测试用例可以保证程序中的每条语句至少执行一次,而且每个条件在执行时都将分别取真、假两种值。,88,步骤,第一步
41、,根据过程设计结果画出相应的流图。 第二步,计算流图的环形复杂度。 第三步,确立线性独立路径的基本集合 所谓独立路径是指至少引入程序的一个新处理语句集合或一个新条件的路径,也就是说,独立路径至少包含一条在定义该路径之前不曾用过的边。 使用基本路径测试法设计测试用例时,程序的环形复杂度决定了程序中独立路径的数量,而且这个数是确保程序中所有语句至少被执行一次所需的测试数量的上界。,89,步骤,第四步,设计可强制执行基本集合中每条路径的测试用例。,90,91,分析例子,流图 环形复杂度 6 基本集合,92,测试用例 路径1的测试用例:valuek有效输入值,其中ki(i的定义在下面)valuei=一
42、999,其中2i100预期结果:基于k的正确平均值和总数注意,路径1无法独立测试,必须作为路径4、5和6的一部分来测试。路径2的测试用例:value1-999预期结果:average一999,其他都保持初始值路径3的测试用例:试图处理101个或更多个值前100个数值应该是有效输人值预期结果:前100个数的平均值,总数为100注意,路径3也无法独立测试,必须作为路径4、5和6的一部分来测试。,93,路径4的测试用例:valuei有效输入值,其中imaximum,其中ki预期结果:基于k的正确平均值和总数路径6的测试用例:valuei有效输入值,其中i100预期结果:正确的平均值和总数,94,2.
43、 条件测试,基本路径测试简单高效,但是只有这样的技术还不够,为了进一步提高白盒测试的质量,还需要使用其他控制结构技术。 条件测试技术设计出的测试用例,能够检查程序模块中包含的逻辑条件。 条件测试基础 简单条件格式:一个布尔变量或一个关系表达式,在布尔变量或关系表达式之前还可能有一个NOT算符。关系表达式的形式如下:E1E2 关系算符:,= 复合条件:复合条件由两个或多个简单条件、布尔算符和括弧组成。布尔算符有OR,AND和NOT。不包含关系表达式的条件称为布尔表达式。 条件成分类型:包括布尔算符、布尔变量、布尔括弧、关系算符及算术表达式。,95,条件错误类型,布尔算符错(布尔算符不正确,遗漏布
44、尔算符或有多余的布尔算符) 布尔变量错 布尔括弧错 关系算符错 算术表达式错,96,条件测试的准则,条件测试的目的不仅是检测程序条件中的错误,而且是检测程序中的其他错误。如果程序P的测试集能有效地检测P中条件的错误,则它很可能也可以有效地检测P中的其他错误。 域测试要求对一个关系表达式执行3个或4个测试。对于形式为E1E2的关系表达式来说,需要3个测试分别使E1的值大于、等于或小于E2的值。如果错误而E1和E2正确,则这3个测试能够发现关系算符的错误。为了发现E1和E2中的错误,让E1值大于或小于E2值的测试数据应该使这两个值之间的差别尽可能小(最好能在边界上取值)。,97,BRO条件测试,前
45、提:在条件中所有布尔变量和关系算符都只出现一次而且没有公共变量,则该测试保证能发现该条件中的分支错和关系算符错。 描述:包含n个简单条件的条件C的条件约束定义为(D1,D2,Dn),其中Di(0in)表示条件C中第i个简单条件的输出约束。如果在条件C的一次执行过程中,C中每个简单条件的输出都满足D中对应的约束,则称C的这次执行覆盖了C的条件约束D。,98,例子1,C1:B1&B2 其中,B1和B2是布尔变量。C1的条件约束形式为(D1,D2),其中D1和D2中的每一个都是“t”或“f”。值(t,f)是C1的一个条件约束,并由使B1值为真B2值为假的测试所覆盖。BRO测试策略要求,约束集(t,t
46、),(f,t),(t,f)被C1的执行所覆盖。如果C1因布尔算符错误而不正确,则至少上述约束集中的一个约束将迫使C1失败。,99,例子2,C2:B1&(E3E4) 其中,B1是布尔变量,E3和E4是算术表达式。C2的条件约束形式为(D1,D2),其中D1是“t”或“f”,D2是,或”,因此,分别用(t,)和(f,)替换(t,t)和(f,t),并用(t,)替换(t,f),就得到C:的约束集(t,=),(f,),(t,)。覆盖上述条件约束集的测试,保证可以发现C2中布尔算符和关系算符的错误。,100,例子3,C3:(E1E2)&(E3E4) 其中, E1,E2,E3和E4是算术表达式。C3的条件约
47、束形式为(D1,D2),而D1和D2的每一个都是,或,),(, ),(,)覆盖上述条件约束集的测试,保证可以发现C3中关系算符的错误。,101,3循环测试,循环测试是一种白盒测试技术,它专注于测试循环结构的有效性。在结构化的程序中通常只有3种循环,即: 简单循环 串接循环 嵌套循环。,102,(1)简单循环测试法,跳过循环。 只通过循环一次。 通过循环两次。 通过循环m次,其中m n一1。 通过循环n一1,n, n +1次,103,(2)嵌套循环测试法,从最内层循环开始测试,把所有其他循环都设置为最小值。对最内层循环使用简单循环测试方法,而使外层循环的迭代参数(例如,循环计数器)取最小值,并为
48、越界值或非法值增加一些额外的测试。由内向外,对下一个循环进行测试,但保持所有其他外层循环为最小值,其他嵌套循环为“典型”值。继续进行下去,直到测试完所有循环。,104,(3)串接循环测试法,各个循环彼此独立:可以使用前述的简单循环测试法。 各个循环彼此不独立: 采用嵌套循环测试法。,105,7.7 黑盒测试技术,黑盒测试重点测试软件功能。它很可能发现白盒测试不易发现的其他类型的错误。是与白盒测试互补的测试方法。 黑盒测试力图发现下述类型的错误: 功能不正确或遗漏了功能; 界面错误; 数据结构错误或外部数据库访问错误; 性能错误; 初始化和终止错误。,106,设计黑盒测试方案时,应该考虑问题:,
49、(1)怎样测试功能的有效性?(2)哪些类型的输入可构成好测试用例?(3)系统是否对特定的输入值特别敏感?(4)怎样划定数据类的边界?(5)系统能够承受什么样的数据率和数据量?(6)数据的特定组合将对系统运行产生什么影响?,107,应用黑盒测试技术的优点,可以减少为达到合理测试所需要设计的测试用例的总数 可以发现是否存在某种类型的错误,而不是仅仅指出与特定测试相关的错误是否存在;,108,7.7.1 等价划分,等价划分是用黑盒法设计测试方案的一种技术,这种技术把程序的输入域划分成若干个数据类,据此导出测试用例,一个理想的测试用例能独自发现一类错误。 前面讲过,穷尽的黑盒测试需要使用所有有效的和无效的输入数据来测试程序,通常这是不现实的。 因此,只能选取少量最有代表性的输入数据,以期用较小的代价暴露出较多的程序错误。,109,等价划分,如果把所有可能的输入数据(有效的和无效的)划分成若干个等价类,则可以合理地做出下述假定: 每类中的一个典型值在测试中的作用与这一类中所有其他值的作用相同。 因此,可以从每个等价类中只取一组数据作为测试数据。 这样选取的测试数据最有代表性,最可能发现程序中的错误。,110,