961全部内容链接
软件测试的目的:
软件测试的重要原则:
测试用例(test case)是由一个输入数据和预期结果组成,测试时通过输入数据,运行被测程序,如果运行的实际输出与预期结果不一致,则表明发现了程序中的错误。
测试用例的原则:
单元测试的基本概念:
单元测试的内容:
单元测试过程:
集成测试也称组装测试、联合测试。集成测试是对集成后的软件系统进行测试。经单元测试后,每个模块都能独立工作,但把它们放在一起往往不能正常工作。主要原因在于:
集成的两种方式:
增量式集成分为自顶向下集成和自底向上集成:
自顶向下集成:从主控模块(主程序)开始,然后按照程序结构图的控制层次,将直接或间接从属于主控模块的模块按深度优先或广度优先的方式逐个集成到整个结构中,并对其进行测试。
如图该程序模块,其中M1为主程序模块(可以理解为Main函数)。如果采用深度优先的顺序,则集成测试顺序为:M1,M2,M5,M8,M6,M3,M7,M4。若采用广度优先测试顺序,则为:M1,M2,M3,M4,M5,M6,M7,M8。
自顶向下集成的步骤为:
1. 主控模块(主程序)被直接用作驱动程序,所有直接从属于主控模块的模块用桩模块替换,然后对主控模块进行测试
2. 根据集成的实现方式(深度优先或广度优先),下层的桩模块一次一个地替换成真正的模块,从属于该模块的模块用桩模块替换,然后对其进行测试
3. 用回归测试来保证没有引入新的错误
4. 重复第(2)和第(3)步,直至所有模块都被集成
自顶向下集成的优点:不需要驱动模块;能尽早对程序的主要控制和决策机制进行检验,能较早发现整体性的错误;深度优先的自顶向下集成能较早对某些完整的程序功能进行检查
自顶向下集成的缺点:测试时低层模块用桩模块替代,不能反映真实情况;重要数据不能及时回送到上层模块
自底向上集成:从程序结构的最底层模块(即原子模块)开始,然后按照程序结构图的控制层次将上层模块集成到整个结构中,并对其进行测试。自底向上集成在测试一个模块时,它的下层模块(已测试过)可用作它的桩模块
自底向上集成的步骤:
自底向上集成的优点:不需要桩模块,所以容易组织测试;将整个程序结构分解成若干个簇,对同一层次的簇可并行进行测试,可提高效率
自底向上集成的缺点:整体性的错误发现得较晚
自顶向下和自底向上应结合起来使用: 自顶向下集成测试与自底向上集成测试各有优缺点,其中一种策略的优点差不多就是另一种策略的缺点。将这两种策略组合起来可能是一种最好的折衷,这种折衷的策略是:在程序结构的高层使用自顶下向策略,而在低层则使用自底向上策略,这种测试策略也称为三明治测试(sandwich testing)
关键模块的概念:集成测试时应特别关注关键模块(critical module)的测试。关键模块是指具有下列一个或多个特征的模块:
关键模块应尽早测试,回归测试时也应集中在关键模块的功能上
确认测试就是系统在要交付之前,做最后确认的测试。
确认测试的标准:确认测试以软件需求规约为依据,以发现软件与需求不一致的错误。主要检查软件是否实现了规约规定的全部功能要求,文档资料是否完整、正确、合理,其他的需求,如可移植性、可维护性、兼容性、错误恢复能力等是否满足
确认测试的结果分为两类:
软件配置评审:软件配置评审也称软件审核(audit),其目的是保证软件配置的所有成分都齐全,各方面的质量都符合要求,具有维护阶段必需的细节,而且已经编排好分类目录
软件配置主要包括计算机程序(源代码和可执行程序),针对开发者和用户的各类文档,包含在程序内部或程序外部的数据
如果软件是为一个客户开发的,那么,最后由客户进行验收测试(acceptance test),以使客户确认该软件是他所需要的。
如果软件是针对许多客户(如腾讯QQ),需要经历α测试和β测试:
系统测试是对整个基于计算机的系统进行的一系列测试。
主要有以下几种:
恢复测试是通过各种手段,强制软件发生故障,然后来测试系统能否在指定的时间间隔内恢复正常,包括修正错误并重新启动系统
如果恢复是由系统自身来完成的,那么,需测试重新初始化、检查点机制、数据恢复和重启动等的正确性
如果恢复需要人工干预,那么要估算平均修复时间MTTR(mean time to repair)是否在用户可以接受的范围内
安全保密性测试(也称安全测试)用来验证集成在系统中的保护机制能否实际保护系统不受非法侵入
在安全测试过程中,测试者扮演一个试图攻击系统的角色,采用各种方式攻击系统。例如,截取或码译密码;借助特殊软件攻击系统;“制服”系统,使他人无法访问;故意导致系统失效,企图在系统恢复之机侵入系统;通过浏览非保密数据,从中找出进入系统的钥匙等等
一般来说,只要有足够的时间和资源,好的安全测试一定能最终侵入系统。系统设计者的任务是把系统设计成:攻破系统所付出的代价大于攻破系统后得到信息的价值
压力测试也称强度测试,它是在一种需要非正常数量、频率或容量的方式下执行系统,其目的是检查系统对非正常情况的承受程度。例如:
性能测试用来测试软件在集成的系统中的运行性能。它对实时系统和嵌入式系统尤为重要。
性能测试可以发生在测试过程的所有步骤中:
性能测试常常需要与压力测试结合起来进行,而且常常需要一些硬件和软件测试设备,以监测系统的运行情况
回归测试就是回去重新再测一遍。
在集成测试过程中,每当增加一个(或一组)新模块时,原先已集成的软件就发生了改变。新的数据流路径被建立,新的I/O操作可能出现,还可能激活新的控制逻辑,这些改变可能使原本正常的功能产生错误
当测试时发现错误后,需修改程序;或者在软件维护时也需修改程序。这些对程序的修改也可能使原本正常的功能产生错误
回归测试就是对已经进行过测试的测试用例子集的重新测试,以确保对程序的改变和修改,没有传播非故意的副作用
回归测试集(已经通过测试的测试用例子集)包括三种不同类型的测试用例:
测试的目的是发现错误,调试(也称排错)的目的是确定错误的原因和准确位置,并加以纠正
调试有如下几种方法:
蛮力法是一种最省脑筋但又最低效的方法。它通过在程序中设置断点,输出寄存器、存储器的内容,打印有关变量的值等手段,获取大量现场信息,从中找出错误的原因
这种方法效率低,输出的信息大多是无用的,通常在其他调试方法未能找到错误原因时,才使用这种方法
可以采用二分法来逐步缩小出错的范围
回溯法是从错误的征兆出发,人工沿着控制流程往回跟踪,直至发现错误的根源。这种方法适用于小型程序,对大型程序,由于回溯的路径太多,难以彻底回溯。
就是把自己想成机器,然后一步一步走,看哪一步会出错。所以如果程序偏大, 这个就不合适了。
原因排除法又可分为归纳法和演绎法。
归纳法是一种从特殊推断一般的系统化思考方法。其基本思想是:从一些线索(错误征兆)着手,通过分析它们之间的关系来找出错误的原因。如下图所示:
演绎法从一般原理或前提出发,假设所有可能出错的原因,排除不可能正确的假设,最后推导出结论。如图所示:
这个从上面的概念中可以看出。这里我自己做一个总结:测试是测试代码是否出错,是否符合预期。而调试是当结果不符合预期时,通过一些方法手段,找出错误原因,并加以修改。
白盒测试(又称为结构测试)把测试对象看作一个透明的盒子,测试人员根据程序内部的逻辑结构及有关信息设计测试用例,检查程序中所有逻辑路径是否都按预定的要求正确地工作。
白盒测试主要用于对模块的测试,包括:
在实际问题中,一个不太复杂的程序,特别是包含循环的程序,其路径数可能非常大。因此测试常常难以做到覆盖程序中的所有路径,为此,我们希望把测试的程序路径数压缩到一定的范围内
基本路径测试首先根据程序或设计图画出控制流图,并计算其区域数,然后确定一组独立的程序执行路径(称为基本路径),最后为每一条基本路径设计一个测试用例
流图由结点和边组成,分别用圆和箭头表示。设计图中一个连续的处理框(对应于程序中的顺序语句)序列和一个判定框(对应于程序中的条件控制语句)映射成流图中的一个结点,设计图中的箭头(对应于程序中的控制转向)映射成流图中的一条边。对于设计图中多个箭头的交汇点可以映射成流图中的一个结点(空结点)
上述映射的前提是设计图的判定中不包含复合条件。如果设计图的判定中包含了复合条件,那么必须先 将其转换成等价的简单条件设计图
黑盒测试(又称行为测试)把测试对象看做一个黑盒子,测试人员完全不考虑程序内部的逻辑结构和内部特性,只依据程序的需求规格说明书,检查程序的功能是否符合它的功能需求
黑盒测试可用于各种测试,它试图发现以下类型的错误:
黑盒测试主要有以下方法:等价类划分、边界值分析、比较测试、错误猜测、因果图
由于不能穷举所有可能的输入数据来进行测试,所以只能选择少量有代表性的输入数据,来揭露尽可能多的程序错误
等价类划分方法将所有可能的输入数据划分成若干个等价类,然后在每个等价类中选取一个代表性的数据作为测试用例。
等价类是指输入域的某个子集,该子集中的每个输入数据对揭露软件中的错误都是等效的,测试等价类的某个代表值就等价于对这一类其他值的测试。也就是说,如果该子集中的某个输入数据能检测出某个错误,那么该子集中的其他输入数据也能检测出同样的错误;反之,如果该子集中的某个输入数据不能检测出错误,那么该子集中的其他输入数据也不能检测出错误
等价类划分方法把输入数据分为有效输入数据和无效输入数据:
在确定输入数据等价类时,常常还要分析输出数据的等价类,以便根据输出数据等价类导出输入数据等价类
根据软件的规格说明,对每一个输入条件(通常是规格说明中的一句话或一个短语)确定若干个有效等价类和若干个无效等价类。一般使用如下表格表示
输入条件 | 有效等价类 | 无效等价类 |
---|---|---|
例题:某编译程序的规格说明中关于标识符的规定如下:标识符是由字母开头,后跟字母或数字的任意组合构成;标识符的字符数为1∽8个;标识符必须先说明后使用;一个说明语句中至少有一个标识符;保留字不能用作变量标识符。
输入条件 | 有效等价类 | 无效等价类 |
---|---|---|
第一个字符 | 字母(1) | 数字(2) 非字母数字字符(3) |
后跟的字符 | 字母(4) 数字(5) |
非字母数字字符(6) 保留字(7) |
字符数 | 1-8个(8) | 0个(9) 大于8个(10) |
标识符的使用 | 先说明后使用(11) | 未说明已使用(12) |
标识符个数 | 大于等于1个(13) | 0个(14) |
测试覆盖度也称覆盖率。主要用来度量测试是否全面。
白盒测试中,经常用到的覆盖率是白盒覆盖率,包含以下几种:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖、路径覆盖。
语句覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每个可执行语句都至少执行一次
语句覆盖率=(至少被执行一次的语句数量)/可执行的语句总数。
判定覆盖(也称分支覆盖)是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每个判定的所有可能结果都至少执行一次(即判定的每个分支至少经过一次)
判定覆盖=(判定结果被评价的次数)/判定结果的总数。
条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每个判定中的每个条件的所有可能结果都至少出现一次
条件覆盖=(条件操作数值至少被评价一次的数量)/(条件操作数值得总数)
注意判定与条件的区别,例如
if (条件1||条件2&&条件3) {
// 分支1
} else {
// 分支2
}
条件指的是if括号内的东西,可以有多个。而判定(分支)指的是当条件成立或不成立后的路径走向。
判定/条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每个判定的所有可能结果都至少执行一次,并且,每个判定中的每个条件的所有可能结果都至少出现一次
判定条件覆盖=(条件操作数值或判定结果至少被评价一次的数量)/(条件操作数值总数+判定结果总数)
条件组合覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每个判定中条件结果的所有可能组合都至少出现一次
显然,满足条件组合覆盖标准的测试用例一定也满足判定覆盖、条件覆盖、判定/条件覆盖、语句覆盖标准
条件组合覆盖=(至少被执行到一次的条件组合)/总的可能的条件组合数。
注意判定/条件覆盖和条件组合覆盖的区别,例如:
if (条件1||条件2&&条件3) {
// 分支1
} else {
// 分支2
}
判定/条件覆盖是条件和判定的总和。只要每个条件都执行过,每个分支也都执行过就可以。比如使用 “条件1=true, 条件2=true,条件3=true” 和 “条件1=false, 条件2=false,条件3=false” 只需要这两中测试数据就可以达到判定/条件全覆盖。因为每一个分支和每一个条件都执行过。
而条件组合覆盖强调的是组合。比如上述两种情况是两种组合,还可以有 “条件1=false, 条件2=true,条件3=true”这样的组合。条件越多,组合数越多。所以说条件组合判定是上述5种覆盖标准中最强的一种。因为只要这个全覆盖了,上面都全覆盖了。
路径覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每条可能执行到的路径都至少经过一次(如果程序中包含环路,则要求每条环路至少经过一次)
路径覆盖实际上考虑了程序中各种判定结果的所有可能组合,但它未必能覆盖判定中条件结果的各种可能情况。因此,它是一种比较强的覆盖标准,但不能替代条件覆盖和条件组合覆盖标准
路径覆盖=(至少被执行到一次的路径数)/总的路径数。
圈复杂度(Cyclomatic Complexity)是用来描述一段代码的复杂程度用的。如果圈复杂度越低说明代码写的越好。
计算代码圈复杂度要进行如下几个步骤:
计算代码圈复杂度的公式为: M = E − N + 2 P M=E-N + 2P M=E−N+2P
其中:
关于P,网上很多文章给出的不一致。但实际效果是一样的。比如典型的有:
- return和throw的数量之和
- 使程序结束的节点
- 更专业点,还有:连通分量的个数
他们其实表示的是一个东西,计算下来也都一样。
个人感觉(没找到具体资料验证):最终求的M就是该程序段的路径数。