Download presentation
Presentation is loading. Please wait.
1
第7章 实现 7.1 编码 7.2 软件测试基础 7.3 单元测试 7.4 集成测试 7.5 确认测试 7.6 白盒测试技术
第7章 实现 7.1 编码 7.2 软件测试基础 7.3 单元测试 7.4 集成测试 7.5 确认测试 7.6 白盒测试技术 7.7 黑盒测试技术 7.8 调试 7.9 软件可靠性 1
2
§1 编码 软件质量主要取决于设计质量。编码的任务相对简单,就是将详细设计文档忠实翻译成程序代码。
§1 编码 软件质量主要取决于设计质量。编码的任务相对简单,就是将详细设计文档忠实翻译成程序代码。 从如何提高软件的质量和可维护性的角度,讨论在编码阶段所要解决的主要问题: 程序设计语言的特性及选择的原则 编码风格 2
3
高级语言的分类 基础语言(经典程序设计语言) 结构化语言
FORTRAN语言:第一个实现的高级语言。它采用与数学表达类似的方法描述算法,主要用于工程设计与数值计算。 COBOL语言:商业数据处理领域的重要算法语言。 BASIC语言:计算机应用普及的理想入门语言。 ALGOL语言:第一个提供丰富的数据类型和控制结构,支持动态存储分配、递归程序设计的高级语言。主要用于学术、教育领域。它对后来的结构化语言产生深远的影响。 结构化语言 主要起源于ALGOL。 PL/1:大型通用语言,适应于各种不同领域的应用需要。 PASCAL:70年代程序设计的代表语言。 Ada:结构与符号类似PASCAL,最初主要针对嵌入式实时系统设计开发。 C语言:在系统、应用软件的开发设计中广泛使用。 3
4
面向对象的语言 第四代语言(4GL) Smalltalk:首先实现真正的面向对象的程序设计。
C++: 既融合了面向对象的能力,又与C语言兼容,保留了C的许多重要特性。 Java:是一种简单的面向对象的分布式的语言。JAVA的应用推动了Internet上基于Web服务的应用发展。 C#:面向对象的、运行于.NET Framework之上的高级程序设计语言。主要是从C和C++继承而来的。 第四代语言(4GL) 又称超高级语言,它们通常由某个专门领域的知识库和方法库支持,是数据处理和过程描述的更高级抽象,具有非过程特性。如查询语言、应用程序生成器、原型语言、形式化规格说明语言等。 4
5
选择程序设计语言主要的实用标准 系统用户的要求 可以使用的编译程序 可以得到的软件工具 工程规模程序员的知识 软件可移植性要求
软件的应用领域(选择语言的关键因素) 它一般有5种类型: 科学工程计算:可供选择的语言有Fortran、Pascal、C和PL/1。 数据处理与数据库应用:可供选择的语言有Cobol、SQL和4GL。 实时处理:可供选择的语言有汇编语言和Ada。 系统软件:可供选择的语言有汇编语言、C、Pascal和Ada。 人工智能:可供选择的语言有Prolog和Lisp。 5
6
编码风格 程序实际上也是一种供人阅读的文章,有一个文章的风格问题。应该使程序具有良好的风格。 程序内部的文档 数据说明 语句构造 输入/输出
效率 6
7
1、程序内部的文档 符号名的命名 符号名即标识符,包括模块名、变量名、常量名、标号名、子程序名、 数据区名以及缓冲区名等。
这些名字应能反映它所代表的实际东西,应有一定实际意义。例如,表示次数的量用Times,表示总量的用Total,表示平均值的用Average,表示和的量用Sum等。 名字不是越长越好,应当选择精炼的意义明确的名字。必要时可使用缩写名字,但这时要注意缩写规则要一致,并且要给每一个名字加注释。同时,在一个程序中,一个变量只应用于一种用途。 7
8
程序的注释 1)序言性注释 注释决不是可有可无的。一些正规的程序文本中,注释行的数量占到整个源程序的1/3到1/2,甚至更多。
注释分为序言性注释和功能性注释。 1)序言性注释 通常置于每个程序模块的开头部分,它应当给出程序的整体说明,对于理解程序本身具有引导作用。有些软件开发部门对序言性注释做了明确而严格的规定,要求程序编制者逐项列出有关项目。有关项目包括: 程序标题; 有关本模块功能和目的的说明; 主要算法; 接口说明:包括调用形式,参数描述,子程序清单; 有关数据描述:重要的变量及其用途,约束或限制条件,以及其它有关信息; 模块位置:在哪一个源文件中,或隶属于哪一个软件包; 开发简历:模块设计者,复审者,复审日期,修改日期及有关说明等。 8
9
2)功能性注释 功能性注释嵌在源程序体中,用以描述其后的语句或程序段是在做什么工作,或是执行了下面的语句会怎么样。而不要解释下面怎么做。
例如, /* ADD AMOUNT TO TOTAL */ TOTAL = AMOUNT+TOTAL 不好。 如果注明把月销售额计入年度总额,便使读者理解了语句的意图: /* ADD MONTHLY-SALES TO ANNUAL-TOTAL */ TOTAL = AMOUNT+TOTAL 9
10
3)空格、空行和移行 恰当地利用空格,可以突出运算的优先性,避免发生运算的错误。例如 ,将表达式D*A**B写成 D* A**B
自然的程序段之间可用空行隔开; 移行也叫做向右缩格。它是指程序中的各行不必都在左端对齐,否则会使程序完全分不清层次关系。对于选择语句和循环语句,把其中的程序段语句向右做阶梯式移行。使程序的逻辑结构更加清晰。 IF(…) THEN IF(…) THEN …… ELSE …… ENDIF …… ELSE …… ENDIF 例如: 10
11
2、数据说明 为了使程序中数据说明更易于理解和维护,必须注意以下几点: 1)数据说明的次序应当规范化
数据说明次序规范化,使数据属性容易查找,也有利于测试,排错和维护。 例如,在FORTRAN程序中数据说明次序 常量说明; 简单变量类型说明;数组说明;公用数据块说明…… 在类型说明中还可进一步要求。例如, 可按如下顺序排列: 整型量说明;实型量说明;字符量说明; 逻辑量说明 11
12
2)说明语句中变量安排有序化 3)使用注释说明复杂数据结构 当多个变量名在一个说明语句中说明时,应当对这些变量按字母的顺序排列。 例如,把
integer size, length, width, cost, price 写成 integer cost, length, price , size, width 3)使用注释说明复杂数据结构 如果设计了一个复杂的数据结构,应当使用注释来说明在程序实现时这个数据结构的固有特点。 例如, 对用户自定义的数据类型,都应当在注释中做必要的补充说明。 12
13
3、语句构造 在设计阶段确定了软件的逻辑流结构,但构造单个语句则是编码阶段的任务。语句构造力求简单,直接,不能为了片面追求效率而使语句复杂化。 1)在一行内只写一条语句 许多程序设计语言允许在一行内写多个语句。但这种方式会使程序可读性变差。因而不可取。 在一行内只写一条语句,并且采取适当的移行格式,使程序的逻辑和功能变得更加明确。 2)程序编写首先应当考虑清晰性 程序编写首先应当考虑清晰性,不要刻意追求技巧性,使程序编写得过于紧凑。 13
14
3)程序要能直截了当地说明程序员的用意。 例如,for ( i = 1; i <= n; i++ ) for ( j = 1; j <= n; j++ ) V[i][j] = ( i/j ) * ( j/i ) 分析: 当 i<j 时, i / j = 当 j<i 时, j / i = 0 即 当i≠j时 V[i][j] = ( i/j ) * ( j/i ) = 0 当i=j时 V[i][j] = ( i/j ) * ( j/i ) = 1 这样得到的结果 V 是一个单位矩阵。 写成以下的形式,就能让读者直接了解程序编写者的意图。 for ( i=1; i <= n; i++ ) for ( j=1; j <= n; j++ ) if ( i == j ) V[i][j] = 1; ELSE V[i][j] = 0; 14
15
4)除非对效率有特殊的要求, 程序编写要做到清晰第一,效率第二。 5)首先要保证程序正确, 然后才要求提高速度。 6)尽可能使用库函数。
4)除非对效率有特殊的要求, 程序编写要做到清晰第一,效率第二。 5)首先要保证程序正确, 然后才要求提高速度。 6)尽可能使用库函数。 7)尽量用公共过程或子程序去代替重复的功能代码段。 8)避免使用临时变量而使可读性下降。 9)避免不必要的转移。如果能保持程序可读性,则不必用 GO TO语句。 10)尽量只采用三种基本的控制结构来编写程序。 11)用逻辑表达式代替分支嵌套。 例如,用 if ( char >= ‘0’ && char <= ‘9’ ) … 来代替 if ( char >= '0' ) if ( char <= '9' ) … 15
16
12)避免使用空的ELSE语句和IF…THEN IF…的语句。
这种结构容易使读者产生误解而出现二义性问题。例如: if ( char >= 'a’ ) if ( char <= ’z’ ) cout << “This is a letter。”; else cout << “This is not a letter。”; 13)尽量减少使用“否定”条件的条件语句。 例如,如果在程序中出现 if ( !( char<‘0’ || char >‘9’ ) ) …… 改成 if ( char >= '0’ && char <= '9’ ) …… 不要让读者绕弯子想。 14)不要修补不好的程序,要重新编写。也不要一味地追求代码的复用,要重新组织。 15)对递归定义的数据结构尽量使用递归过程。 16
17
4、输入和输出 输入和输出的方式和格式应当尽可能方便用户的使用。一定要避免因设计不当给用户带来的麻烦。 几条重要原则:
⑴ 输入和输出格式应尽可能统一。 ⑵ 输出信息中应该反映输入的数据,这是检查程序运行正确性和用户输入数据正确性的需要。 (3) 输入和输出应尽可能集中安排,应该把输入输出代码集中在最少的程序段之中。 另外,要特别注意,尽量减少输入数据,提高输入效率,考虑对输入数据进行查错的功能。 17
18
5、程序效率 程序的效率是指程序的执行速度及程序所需占用的内存的存储空间。 讨论程序效率前应记住几条准则:
1 ) 效率是一个性能要求,应当在需求分析阶段给出。软件效率以需求为准,不应以人力所及为准。 2 )好的设计可以提高效率。 3 )程序的效率与程序的简单性相关。 4 )一般说来,任何对效率无重要改善,且对程序的简单性、可读性和正确性不利的程序设计方法都是不可取的。 18
19
§2 软件测试基础 软件测试的定义 软件测试是为了发现错误而执行程序的过程。或者说软件测试是根据软件开发各阶段的规格说明和程序的内部结构而精心设计一批测试用例(即输入数据及其预期的输出结果) ,并利用这些测试用例去运行程序,以发现程序错误的过程。 19
20
软件测试的目标 以最少的时间和人力系统地找出软件中潜在的各种错误和缺陷。 Grenford J.Myers就软件测试目的提出观点:
(1) 测试是程序的执行过程,目的在于发现错误; (2) 一个好的测试用例在于能发现至今未发现的错误; (3) 一个成功的测试是发现了至今未发现的错误的测试。 20
21
软件测试的准则 1. 尽早地和不断地进行软件测试。 2. 测试用例应由测试输入数据和对应的预期输出结果这两部分组成。
3. 程序员应避免检查自己的程序。 4. 在设计测试用例时,应当包括合理的输入条件和不合理的输入条件。 5. 充分注意测试中的群集现象。——80%的错误来源于20%的程序模块 6. 严格执行测试计划,排除测试的随意性。 7. 应当对每一个测试结果做全面检查。 8. 妥善保存测试计划,测试用例,出错统计和最终分析报告,为维护提供方便。 21
22
软件测试的方法 黑盒测试 黑盒法设计基本的测试方案 白盒测试 白盒法补充必要的测试方案
通常情况下不论采用什么方法和技术,其测试都是不彻底的,也是不完全的,因为任何一次完全测试(穷举测试)的工作量太大,在实践上是行不通的,因此任何实际测试都不能够保证被测试程序中不存在遗留的错误。 22
23
黑盒测试法 把程序看作一个黑盒子,完全不考虑程序的内部结构和处理过程。它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据并产生正确的输出信息,程序运行过程中能否保持外部信息的完整性。黑盒测试又称为功能测试、数据驱动测试。 白盒测试法 是把程序看成装在一个透明的白盒子里,测试者完全知道程序的结构和处理算法。这种方法按照程序内部的逻辑测试程序,检测程序中的主要执行通路是否都能按预定要求正确工作。白盒测试又称为结构测试、基于代码的测试、逻辑驱动测试、玻璃盒测试等。 23
24
黑盒测试 假设一个程序P有输入量X和Y及输出量Z。在字长为32位的计算机上运行。若X、Y取整数,按黑盒方法进行穷举测试: 可能采用的
测试数据组: 232×232 =264 如果测试一 组数据需要1毫秒,一年工作365× 24小时,完成所有测试约需5亿年。 24
25
白盒测试 对一个具有多重选择和循环嵌套的程序,不同的路径数目可能是天文数字。
例如:如下一个小程序的流程图,它包括了一个执行20次的循环。包含的不同执行路径数达520条,对每一条路径进行测试需要1毫秒,假定一年工作365 × 24小时,要想把所有路径测试完,约需3千年。 25
26
软件测试的步骤 模块测试(单元测试) 集成测试(组装测试) 验收测试(确认测试) 系统测试 平行运行 26
27
§3 单元测试 1. 单元测试的重点 ——又称模块测试 注意是否能够对错误定位,对错误条件的处理正确与否等。
如果数据不能正确地输入和输出,其他测试都无法进行。 通常是模块错误的来源。 最基本、最主要的任务,保证内部的正确性。 注意数据流、控制流中刚好等于、大于或小于确定的比较值时出错的可能性。 27
28
2. 代码审查 开会、讲解实现细节和逻辑,由审查小组审查。 实践证明:某些错误人工审查更容易发现
对于典型的程序来说,可以查出30%~70%的逻辑设计错误和编码错误。 审查小组最好由4人组成: (1) 组长,应该是一个很有能力的程序员,而且没有直接参与这项工程; (2) 程序的设计者; (3) 程序的编写者; (4) 程序的测试者。 28
29
3. 计算机测试 驱动模块 (driver) ── 相当于所测模块的主程序。它接收测试数据,把这些数据传送给所测模块,最后再输出实测结果
桩模块 (stub) ── 存根模块。用以代替所测模块调用的子模块。 29
30
§4 集成测试 集成测试 (组装测试、联合测试、综合测试) 通常,在单元测试的基础上,需要将所有模块按照设计要求组装成为系统。
§4 集成测试 集成测试 (组装测试、联合测试、综合测试) 通常,在单元测试的基础上,需要将所有模块按照设计要求组装成为系统。 子系统的组装测试特别称为部件测试。 通常,把模块组装成为系统的方式有两种 一次性组装方式(整体拼装,非渐增式,非增殖式) 增殖式组装方式(渐增式) 30
31
一次性组装方式 首先对每个模块分别进行模块测试,然后再把所有模块组装在一起进行测试,最终得到要求的软件系统。 31
32
首先对各个模块进行模块测试,然后将这些模块边连接边测试,逐步组装成为要求的软件系统。
增殖式组装方式 首先对各个模块进行模块测试,然后将这些模块边连接边测试,逐步组装成为要求的软件系统。 1、自顶向下的集成 将模块按系统结构,沿控制层次自顶向下进行组装。 32
33
这种组装的方式是从系统结构的最底层的模块开始组装和测试。
2、自底向上的集成 这种组装的方式是从系统结构的最底层的模块开始组装和测试。 33
34
优点:不需设计存根模块,设计测试驱动模块一般比建立存根模块要容易;且可实现多个模块的并行测试。
3、不同集成测试策略的比较 自顶向下 优点:不需设计测试驱动模块。 缺点:需设计存根模块,复杂度较高。 自底向上 优点:不需设计存根模块,设计测试驱动模块一般比建立存根模块要容易;且可实现多个模块的并行测试。 缺点:直到最后一个模块结合进来,程序作为一个整体才存在。即,对主要的控制直到最后才接触到。 通常根据情况用混合法进行组装和测试:对软件结构中较上层模块使用自顶向下结合方法,对软件结构中较下层模块使用自底向上结合方法。 34 34
35
每当一个新模块作为集成测试的一部分加进来的时候,软件就发生了些变化,可能使原来工作正常的功能出现问题。
4、回归测试 每当一个新模块作为集成测试的一部分加进来的时候,软件就发生了些变化,可能使原来工作正常的功能出现问题。 回归测试是指重新执行已执行过的测试用例的某个子集(回归测试集),以保证这些变化没有带来非预期的副作用。 回归测试集包括下述3类不同的测试用例: (1) 检测软件全部功能的代表性测试用例; (2) 专门针对可能受修改影响的软件功能的附加测试; (3) 针对被修改过的软件成分的测试。 35
36
§5 确认测试 1. 确认测试的范围 确认测试的任务:验证软件的功能和性能及其它特性是否与用户的要求一致。
在模拟的环境 (可能就是开发的环境) 下,运用黑盒测试的方法,验证被测软件是否满足需求规格说明书列出的需求。 测试结果可以分为两类: 测试结果与预期的结果相符。 测试结果与预期的结果不符。 必须有用户参与 36
37
2. 软件配置复查 3.α测试和β测试 软件和文档是否齐全以及分类是否有序。 确保文档、资料的正确和完善。 α测试(Alpha)
软件配置复查的目的是保证: 软件和文档是否齐全以及分类是否有序。 确保文档、资料的正确和完善。 3.α测试和β测试 α测试(Alpha) 在开发者的场所由用户进行,在开发者关注和控制的环境下进行。 β测试(Beta) 软件的最终用户在一个或多个用户场所进行。 37
38
§6 白盒测试技术 1、逻辑覆盖 逻辑覆盖是以程序内部的逻辑结构为基础的设计测试用例的技术。 语句覆盖 判定覆盖 条件覆盖 判定/条件覆盖
条件组合覆盖 点覆盖 边覆盖 路径覆盖 38
39
1)语句覆盖 举例: 所有的语句至少执行一次。 满足语句覆盖的测试用例是: 【(2, 0, 4),(2, 0, 3)】覆盖【 L1 】
1)语句覆盖 举例: 所有的语句至少执行一次。 满足语句覆盖的测试用例是: 【(2, 0, 4),(2, 0, 3)】覆盖【 L1 】 语句覆盖对程序的逻辑覆盖很少。 只关心判定表达式的值,而没分别测试判定表达式中每个条件取不同值时的情况。 可能发现不了判断中逻辑运算中出现的错误。 如:and 变成 or X>1 变成 X<1 语句覆盖是很弱的逻辑覆盖准则。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 39
40
2)判定覆盖(又称为分支覆盖) 举例: 每个语句至少执行一次,且每个判断的取真分支和取假分支至少经历一次。
若选择路径L1和L2,就可得满足要求的一组测试用例: 【(2, 0, 4),(2, 0, 3)】覆盖 【L1】 【(1, 1, 1),(1, 1, 1)】覆盖 【L2】 若选择路径L3和L4,还可得另一 组可用的测试用例: 【(2, 1, 1),(2, 1, 2)】覆盖 【L3】 【(3, 0, 3),(3, 1, 1)】覆盖 【L4】 测试用例取法不唯一。 判定覆盖比语句覆盖强,但对程序逻辑的覆盖程度仍然不高,例如,上面的测试数据只覆盖了程序全部路径的一半。 把第二个判断中的条件x>1错写成x<1,上述两组测试用例均不能发现错误。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 40
41
3)条件覆盖 举例: 每个语句至少执行一次,而且判定表达式中的每个条件都要取得各种可能的结果。 设 条件 A>1 为T1
条件 B=0 为T2 条件 A=2 为T3 条件 X>1 为T4 满足要求的一组测试用例: 【(2, 0, 4),(2, 0, 3)】覆盖 【L1】T T T T 【(1, 1, 1),(1, 1, 1)】覆盖 【L2】F F F F 满足要求的另一组测试用例: 【(2, 0, 1),(2, 0, 1)】覆盖 【L1】T T T F 【(1, 1, 2),(1, 1, 3)】覆盖 【L3】FF F T 前一组测试用例满足条件覆盖,同时也满足判定覆盖。但后一组测试用例虽满足了条件覆盖,但不满足判定覆盖的要求。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 41
42
4)判定/条件覆盖 举例: 既满足判定覆盖也满足条件覆盖的要求。
满足要求的一组测试用例: 【(2, 0, 4),(2, 0, 3)】覆盖 【L1】 【(1, 1, 1),(1, 1, 1)】覆盖 【L2】 有时判定/条件覆盖也并不比条件覆盖更强。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 42
43
5)条件组合覆盖 举例: 使得每个判断的所有可能的条件取值组合至少执行一次。
满足要求的一组测试用例: 【(2, 0, 4), (2, 0, 3)】覆盖【 L1 】T T T T 【(2, 1, 1), (2, 1, 2)】覆盖【 L3 】 T F T F 【(1, 0, 3), (1, 0, 4)】覆盖【 L3 】 F T F T 【(1, 1, 1), (1, 1, 1)】覆盖【 L2 】 F F F F 满足条件组合覆盖,也一定满足判定覆盖、条件覆盖和判定/条件覆盖标准。因此,条件组合覆盖是前述几种覆盖标准中最强的。 满足条件组合覆盖并不一定能使程序中的每条路径都执行到。如这组测试用例漏掉了路径L4。测试还不完全。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 43
44
从对程序路径的覆盖程度分析,能够提出下述一些主要的逻辑覆盖标准:
6)点覆盖(同语句覆盖) 图论中点覆盖的概念定义如下:如果连通图G的子图G′是连通的,而且包含G的所有结点,则称G′是G的点覆盖。 在正常情况下流图是连通的有向图。 点覆盖要求程序执行路径至少经过流图的每个结点一次。 由于流图的每个结点与一条或多条语句相对应,显然,点覆盖标准和语句覆盖标准是相同的。 7)边覆盖(同判定覆盖) 图论中边覆盖的定义是:如果连通图G的子图G″是连通的,而且包含G的所有边,则称G″是G的边覆盖。 边覆盖要求程序执行路径至少经过流图中每条边一次。 通常边覆盖和判定覆盖是一致的。 44
45
8)路径覆盖 举例: 使程序的每条可能路径都至少执行一次。
满足要求的一组测试用例: 【(2, 0, 4), (2, 0, 3)】覆盖 【 L1 】T T T T 【(1, 1, 1), (1, 1, 1)】覆盖 【 L2 】F F F F 【(1, 1, 2), (1, 1, 3)】覆盖 【 L3 】T T T F 【(3, 0, 3), (3, 0, 1)】覆盖 【 L4 】T T F F 路径覆盖是最强的覆盖准则。但在路径数目很大时,真正做到完全覆盖是很困难的,必须把覆盖路径数目压缩到一定限度。 测试用例的设计格式: 【输入的(A, B, X),输出的(A, B, X)】 所有路径为:L1(sacbed) ,L2(sabd), L3(sabed), L4(sacbd) 45
46
2、控制结构测试 基本路径测试 条件测试 循环测试 根据路径设计专门的测试用例。 对布尔表达式设计专门的测试用例。
对循环结构设计专门的测试用例。 46
47
基本路径测试 基本路径测试方法把覆盖的路径数压缩到一定限度内。它是在程序流图的基础上,分析环路复杂性,导出基本可执行路径集合,设计测试用例的方法。设计出的测试用例要保证在测试中,程序的每一个可执行语句至少要执行一次。 47
48
此法设计测试用例的步骤: 第一步:根据过程设计结果画出相应的流图。
第二步:计算流图的环形复杂度。 本例流图的环形复杂度为6。 48
49
第四步:导出测试用例,确保基本路径集中的每一条路径的执行。
第三步:确定线性独立路径的基本集合。 由于环形复杂度为6,因此共有6条独立路径。 例如,在本例的流图中,一组独立的路径是: path1: path2: path3: path4: … path5: … path6: … 路径 path1~path6组成了控制流图的一个基本路径集。 第四步:导出测试用例,确保基本路径集中的每一条路径的执行。 49
50
BRO(branch and relational operator)条件测试策略:
BRO测试利用条件C的条件约束来设计测试用例。包含n个简单条件的条件C的条件约束定义为(D1,D2,…,Dn),其中Di(0<i≤n)表示条件C中第i个简单条件的输出约束。如果在条件C的一次执行过程中,C中每个简单条件的输出都满足D中对应的约束,则称C的这次执行覆盖了C的条件约束D。 对于布尔变量来说,必须是真(t)或假(f)。对于关系表达式来说,用符号>,=和<指定表达式的输出约束。 50
51
循环分为3种不同类型:简单循环、嵌套循环、串接循环(连锁循环)。
循环测试 循环分为3种不同类型:简单循环、嵌套循环、串接循环(连锁循环)。 (1) 简单循环 ① 零次循环 ② 一次循环 ③ 二次循环 ④ m次循环 ⑤ 最大次数循环、比最大次数多一次、少一次的循环。 51
52
(2) 嵌套循环. ① 对最内层循环做简单循环的全部测试。所有其它层的循环变量置为最小值;
(2) 嵌套循环 ① 对最内层循环做简单循环的全部测试。所有其它层的循环变量置为最小值; ② 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小值,所有其它嵌套内层循环的循环变量取“典型”值。。 ③ 反复进行,直到所有各层循环测试完毕。 ④ 对全部各层循环同时取最小循环次数,或者同时取最大循环次数。 52
53
(3) 串接循环 如果各个循环互相独立,则可以用与简单循环相同的方法进行测试。但如果几个循环不是互相独立的,则需要使用测试嵌套循环的办法来处理。
53
54
§7 黑盒测试技术 黑盒测试力图发现下述类型的错误: ①功能不正确或遗漏了功能; ②界面错误; ③数据结构错误或外部数据库访问错误;
④性能错误; ⑤初始化和终止错误。 白盒测试在测试过程的早期阶段进行,而黑盒测试主要用于测试过程的后期。 几种方法: 等价类划分法 边界值法 错误推测法 54
55
1、等价划分 等价类是指某个输入域的子集合。在该子集合中,各个输入数据对于揭露程序中的错误都是等效的。测试某等价类的代表值就等价于对这一类其它值的测试。 设计测试方案时,要同时考虑等价类划分的两种不同的情况: ① 有效等价类 ② 无效等价类 等价划分方法把所有可能的输入数据划分成若干部分,然后从每一部分中选取少数有代表性的数据作为测试用例。 55
56
例1:一个程序计算任何一个在1~1000内整数的平方数,那么其输入项的等价类划分为
例2:Windows文件名可以包含255个字符,但不能包含正斜杠(/)、反斜杠(\)、大于号(>)、小于号(<)、星号(*)、问号(?)、引号(“)、竖线(|)、冒号(:) 或分号(;)等字符,那么文件名的等价类划分为 56
57
例3:对招干考试系统“输入学生成绩”子模块设计测试用例。招干考试分三个专业,准考证号第一位为专业代号,如: 1-行政专业, 2-法律专业, 3-财经专业.
行政专业准考证号码为:110001~111215 法律专业准考证号码为:210001~212006 财经专业准考证号码为:310001~314015 则:准考证号码的等价类划分 有效等价类: (1) ~111215 (2) ~212006 (3) ~314015 无效等价类: (4) -∞~110000 (5) ~210000 (6) ~310000 (7) ~+ ∞ 57
58
边界值分析 经验:大量的错误是发生在输入或输出范围的边界上,而不是在输入范围的内部。
例:在做三角形计算时,要输入三角形的三条边长:A、B和C。 这三个数值应当满足 A>0、B>0、C>0、 A+B>C、A+C>B、B+C>A,才能构成三角形。但如果把六个不等式中的任何一个大于号“>”错写成大于等于号“≥”,那就不能构成三角形。问题恰出现在容易被疏忽的边界附近。 选择测试用例时,一定要选择临近边界的合法数据,以及刚刚超过边界的非法数据。也就是说,应该选取刚好等于、稍小于和稍大于等价类边界值的数据作为测试数据。 例:如果文本编辑框允许输入1~255个字符,那么可以选取合法输入为1个字符和255个字符,非法输入为0个字符和256个字符。 58
59
如果输入值的范围,则应该刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值。 例:-1.0~1.0 -1.0,1.0,-1.1,1.1
边界值分析法原则: 如果输入值的范围,则应该刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值。 例:-1.0~1.0 -1.0,1.0,-1.1,1.1 如果输入条件规定了值的个数,则用最大个数,最小个数,最大个数加1,最小个数-1 如果输入条件为一组值,测试案例应当执行其中最大值,最小值,最大值略小的值,最小值略大的值; 例:{2,4,5,8,12,25,33,35,56} 2,4,35,56 对规格说明的每一个输出条件,使用前面三条原则 如果程序的规格说明给出的说明域或输出域是有序集合(有序表或顺序文件等),则应选取集合的第一个元素和最后一个元素作为测试用例; 如果程序中使用了一个内部数据结构,则应选择其边界上的值作为测试用例。 59
60
错误推测法 凭借经验、直觉和预感测试软件中可能存在的各种错误,从而有针对性设计测试用例。
例1:输入数据为0,或输出数据为0是容易发生错误的情形,因此可选择输入数据为0,或使输出数据为0的例子作为测试用例。 例2:测试一个排序程序,可以选择输入空的值、输入一个数据、所有输入数据均相等、所有输入数据有序排列、所有输入数据逆序排列等进行错误推测。 60
61
白盒法和黑盒法优缺点比较: 61
62
§8 调试 调试和测试的区别: 测试:尽可能多的暴露错误 调试:进一步诊断和改正错误 62
63
几种主要的调试方法 调试的关键在于推断程序的错误位置及原因。 1。强行排错 (蛮干法) 例如:
通过内存全部打印来调试。将计算机存储器和寄存器的全部内容打印出来。 在程序特定部位设置打印语句。把打印语句插在出错的源程序的各个关键变量改变部位、重要分支部位、子程序调用部位。 自动调试工具。利用某些程序语言的调试功能或专门的交互式调试工具,分析程序的动态过程。 63
64
2、回溯法调试 确定最先发生“症状”的地方,沿程序的控制流往回追踪源程序代码。是对小型程序寻找错误位置的有效方法。
例如,程序中发现错误处是某个打印语句。通过输出值可推断程序在这一点上变量的值。再从这一点出发,回溯程序的执行过程,反复考虑:“如果程序在这一点上的状态(变量的值)是这样,那么程序在上一点的状态一定是这样...”, 直到找到错误的位置。 64
65
赋值/输入→结果正确:前半部分;结果错误:后半部分
3、原因排除法 对分查找法 已知:变量的若干关键点的正确值 赋值/输入→结果正确:前半部分;结果错误:后半部分 归纳法调试 从个别判断一般。具体步骤如下: 收集有关的数据 组织数据 提出假设 证明假设 65
66
66
67
从一般原理出发,推导出结论。具体步骤如下:
演绎法调试 从一般原理出发,推导出结论。具体步骤如下: 列举所有可能出错原因的假设 利用已有的测试数据,排除不正确的假设 改进余下的假设 证明余下的假设 67
68
软件可靠性的定义 程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。可靠性意味着在0到t这段时间间隔内系统没有失效
§9 软件可靠性 软件可靠性的定义 程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。可靠性意味着在0到t这段时间间隔内系统没有失效 软件的可用性 程序在给定的时间点,按照规格说明书的规定,成功地运行的概率。可用性意味着在时刻t,系统是正常运行的。 68
69
如果在一段时间内,软件系统故障停机时间分别为td1,td2,…,正常运行时间分别为tu1,tu2,…,则系统的稳态可用性为:
Ass=Tup/(Tup+Tdown) 其中 Tup=∑tui,Tdown=∑tdi Ass=MTTF/(MTTF+MTTR) MTTF(Mean Time To Failure ):系统平均无故障时间 MTTR(Mean Time To Repair ):平均维修时间 69
70
估算平均无故障时间的方法 1)符号 ET——测试之前程序中错误总数; IT——程序长度(机器指令总数); τ——测试(包括调试)时间;
Ed(τ)——在0至τ期间发现的错误数; Ec(τ)——在0至τ期间改正的错误数。 70
71
(1) 在类似的程序中,单位长度里的错误数ET/IT近似为常数。美国的一些统计数字表明,通常0.5×10-2≤ET/IT≤2×10-2
2)基本假定 (1) 在类似的程序中,单位长度里的错误数ET/IT近似为常数。美国的一些统计数字表明,通常0.5×10-2≤ET/IT≤2×10-2 也就是说,在测试之前每1000条指令中大约有5~20个错误。 (2) 失效率正比于软件中剩余的(潜藏的)错误数,而平均无故障时间MTTF与剩余的错误数成反比。 (3) 为了简化讨论,假设发现的每一个错误都立即正确地改正了。因此 Ec(τ)=Ed(τ) 剩余的错误数为 Er(τ)=ET-Ec(τ) 单位长度程序中剩余的错误数为: εr(τ)=ET/Ir-Ec(τ)/IT 71
72
经验表明,平均无故障时间与单位长度程序中剩余的错误数成反比,即 MTTF=1/[K(ET/IT-Ec(τ)/IT)]
3)估算平均无故障时间 经验表明,平均无故障时间与单位长度程序中剩余的错误数成反比,即 MTTF=1/[K(ET/IT-Ec(τ)/IT)] 其中K为常数,典型值是200。 Ec=ET-IT/(K×MTTF) 因此,也可以根据对软件平均无故障时间的要求,估计需要改正多少个错误之后,测试工作才能结束。 72
73
4) 估计错误总数的方法 估计ET的两个方法: (1) 植入错误法
假设人为地植入的错误数为Ns,经过一段时间的测试之后发现ns个植入的错误,此外还发现了n个原有的错误。如果可以认为测试方案发现植入错误和发现原有错误的能力相同,则能够估计出程序中原有错误的总数为 N=n/ns×Ns 其中N即是错误总数ET的估计值。 ^ 73
74
在测试过程的早期阶段,由测试员甲和测试员乙分别测试同一个程序的两个副本,由另一名分析员分析他们的测试结果。用τ表示测试时间,假设
(2) 分别测试法 在测试过程的早期阶段,由测试员甲和测试员乙分别测试同一个程序的两个副本,由另一名分析员分析他们的测试结果。用τ表示测试时间,假设 τ=0时错误总数为B0; τ=τ1时测试员甲发现的错误数为B1; τ=τ1时测试员乙发现的错误数为B2; τ=τ1时两个测试员发现的相同错误数为bc。 如果认为测试员甲发现的错误是有标记的,即程序中有标记的错误总数为B1,则测试员乙发现的B2个错误中有bc个是有标记的。假定测试员乙发现有标记错误和发现无标记错误的概率相同,则可以估计出测试前程序中的错误总数为 B0 =B2/bcB1 ^ 74
75
小结 了解语言的一般分类和特点 了解选择程序设计语言的一般原则 理解和掌握编码的风格 理解软件测试的目的和原则 了解测试的一般策略 测试类型
能够熟练使用白盒方法设计测试用例,并能了解黑盒测试的方法 了解调试的方法 75
Similar presentations