软件测试
软件测试定义
- IEEE(国际电子电气工程师协会):
使用人工或自动手段来运行或测定某个系统的过程,其目的在于检验它是否满足规定的需求或是弄清预期结果与实际结果之间的差别。 - 对软件测试人员而言,测试的最好定义是:软件测试是为了发现错误而执行程序的过程。
- 完整定义:
软件测试是发现并指出软件(包含软件经过建模、需求、设计等阶段所产生的大量输出工件及程序代码)中存在缺陷的过程,这个过程指明和标注问题存在的正确位置,详细记录导致问题出现的操作步骤,及时存储当时的错误状态,以上组合在一起便于测试后问题能够准确再现。
软件测试的分类
按照开发阶段划分
- 单元测试:模块测试,检查每个程序单元嫩否正确实现详细设计说明中的模块功能等。
- 集成测试:组装测试,将所有的程序模块进行有序、递增的测试,检验程序单元或部件的接口关系
- 系统测试:检查完整的程序系统能否和系统(包括硬件、外设和网络、系统软件、支持平台等)正确配置、连接,并满足用户需求。
- 确认测试:证实软件是否满足特定于其用途的需求,是否满足软件需求说明书的规定。
- 验收测试:按照项目任务或合同,供需双方签订的验收依据文档进行的对整个系统的测试与评审,决定是否接受或拒收系统。
按照测试技术划分
- 白盒测试:通过对程序内部结构的分析、检测来寻找问题。检查是否所有的结构及逻辑都是正确的,检查软件内部动作是否按照设计说明的规定正常进行。–结构测试
- 黑盒测试:通过软件的外部表现来发现错误,是在程序界面处进行测试,只是检查是否按照需求规格说明书的规定正常实现。
- 灰盒测试:介于白盒测试与黑盒测试之间的测试,关注输出对输入的正确性;同时,也关注内部表现,不像白盒那样详细,只是通过一些表征性现象、事件、标志来判断内部的运行状态。
按照测试实施组织划分
- 开发方测试:开发方通过检测和提供客观证据,证实软件的实现是否满足规定的需求,在开发环境下,开发方对提交的软件进行全面的自我检查。
- 用户测试:在用户的应用环境中,用户通过运行软件,检测软件实现是否符合自己预期的要求,这里指用户的使用性测试。
- 第三方测试:介于软件开发方和用户方之间的测试组织的测试。
什么时候进行测试
测试可以是一个与开发并行的过程,也可以是开发完成某个阶段任务后的的活动,即模块开发结束之后,还可以在各模块装配成为一个完整的程序之后再进行测试。
怎样进行测试
对软件进行测试就是根据软件的功能规范说明和程序实现,利用各种测试方法,生成有效的测试用例,对软件进行测试。
测试停止的标准
- 测试超过了预定的时间,停止测试;
- 执行了所有测试用例但没有发现故障,停止测试;
- 根据单位时间内查出故障的数量决定是否停止测试
V 模型
20 世纪 80 年代后期,Paul Rook 提出,瀑布模型的变种,它反映了测试活动与分析和设计的关系,非常明确的表明了测试过程中存在的不同级别,以及各测试阶段与开发过程中的各阶段的对应关系。
优点
- 单元测试和集成测试应检测程序的执行是否满足软件设计的要求;
- 系统测试应检测系统功能,性能的质量特性是否达到系统要求的指标;
- 验收测试确定软件的实现是否满足用户需要或合同的要求.
缺点
- 仅仅把测试过程作为在需求分析、系统设计及编码之后的一个阶段
- 忽视了测试对需求分析,系统设计的验证,一直到后期的验收测试才被发现。
W 模型
Evolutif 公司提出了 W 模型的概念,增加了软件各开发阶段中应同步进行的验证和确认活动,明确了测试与开发的并行性.
优点
- 测试伴随着整个软件开发周期
- 测试的对象不仅仅是程序,需求、设计和功能同样要测试
- 根据 W 模型的要求,一旦有文档提供,就要及时确定测试的条件、编写测试用例
缺点
- 在 W 模型中,需求、设计、编码等活动被视为串行的,同时,测试和开发活动也保持着一种线性的前后关系,上一阶段完全结束,才可正式开始下一个阶段工作。
- 无法支持迭代、自发性以及变更调整。
软件工程与软件测试的关系
测试在开发阶段的作用
- 项目规划阶段:负责从单元测试到系统测试的整个测试阶段的监控。
- 需求分析阶段:确定测试需求分析、系统测试计划的制定。其中,测试需求分析是对产品生命周期中测试所需求的资源、配置、每阶段评判通过的规约;系统测试计划则是依据软件的需求规格说明书,制定测试计划和设计相应的测试用例。
- 概要设计和详细设计阶段:确保集成测试计划和单元测试计划完成。
- 编码阶段:由开发人员进行自己负责部分的测试代码。
测试用例
测试用例的定义
- 测试用例是为特定的目的而设计的一组测试输入、执行条件和预期的结果。
- 测试用例是执行测试的最小实体。
测试用例的特征
- 最有可能抓住错误的;
- 不重复的、不多余的;
- 一组相似测试用例中最有效的;
- 既不是太简单,也不是太复杂。
测试用例的代表性
能够代表并覆盖各种合理的和不合理的、合法的和非法的、边界的和越界的以及极限的输入数据、操作和环境设置等。
测试结果的可判定性
即测试执行结果的正确性是可判定的,每一个测试用例都应有相应的期望结果。
测试结果的可再现性
即对同样的测试用例,系统的执行结果应当是相同的。
等价类划分法的思想
等价类划分设计方法是把所有可能的输入数据,即程序的输入域划分成若干部分(子集),然后从每一个子集中选取少量具有代表性的数据作为测试用例。
- 定义:将程序的输入域划分为若干部分,然后从每个部分中选取少数代表性数据当作测试用例。
- 原因:由于实现穷举测试的不可能性,只有从大量的可能数据中选取一部分作为测试用例。
- 效果:经过类别划分后,每一类的代表性数据在测试中的作用都等价于这一类中的其他值。
- 手段:在设计测试用例时,在需求说明的基础上划分等价类,列出等价表,从而确定测试用例。
有效等价类
- 对规格说明而言,有意义、合理的输入数据所组成的集合;
- 检验程序是否实现了规格说明预先规定的功能和性能。
无效等价类
- 对规格说明而言,无意义的、不合理的输入数据所组成的集合;
- 检查被测对象的功能和性能的实现是否有不符合规格说明书要求的地方。
如何划分等价类
先从程序的规格说明书中找出各个输入条件,再为每个输入条件划分两个或多个等价类,形成若干的互不相交的子集。
等价类划分步骤
- 先考虑输入数据的类型(合法型和非法型)
- 再考虑数据范围(合法型中的合法区间和非法区间)
- 画出示意图,区分等价类
- 为每一个等价类编号。
- 考虑输出,进行补充
等价类划分法设计测试用例
-
确定等价类
-
建立等价类表,列出所有划分出的等价类
-
从划分出的等价类中按以下的 3 个原则设计测试用例:
- 为每一个等价类规定一个唯一的编号
- 设计一个新的测试用例,使其尽可能多的覆盖尚未被覆盖的有效等价类,重复这一步,直到所有的有效等价类都被覆盖为止。
- 设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步,直到所有的无效等价类都被覆盖为止。
常见等价类划分测试形式
针对是否对无效数据进行测试,可以将等价类测试分为两种:
- 标准等价类测试(也称,一般等价类测试)
- 健壮等价类测试
标准(一般)等价类测试
- 不考虑无效数据值,测试用例使用每个等价类中的一个值;
- 通常,标准等价类测试用例的数量和最大等价类中元素的数目相等。
健壮等价类测试
- 出发点考虑了无效等价类;
- 对有效输入,测试用例从每个有效等价类中取一个值;对无效输入,一个测试用例有一个无效值,其他值均取有效值
常见的等价类划分测试形式
- 弱一般等价类测试
特点: 不考虑无效数据,测试用例使用每个等价类中的一个值 - 强一般等价类测试
特点:每一个有效等价类要选择至少一个测试用例 - 弱健壮等价类测试
- 对于有效输入: 使用每个有效类的一个值
- 对于无效输入: 测试用例只使用一个无效值,其余值都是有效的
- 强健壮等价类测试
每个有效等价类和无效等价类都至少要选择一个测试用例
等价类的划分原则
- 按照区间划分
在输入条件规定了取值范围或值的个数的情况下,可以确定一个有效等价类和两个无效等价类。 - 按照数值划分
在规定了一组输入数据(假设包括 n 个 输入值),并且程序要对每一个输入值分别进行处理的情况下,可确定 n 个有效等价类(每个值确定一个有效等价类)和一个无效等价类(所有不允许的输入值的集合)。 - 按照数值集合划分
在输入条件规定了输入值的集合或规定了“必须如何”的条件下,可以确定一个有效等价类和一个无效等价类(该集合有效值之外)。 - 按照限制条件或规则划分
在规定了输入数据必须遵守的规则或限制条件的情况下,可确定一个有效等价类(符合规则)和若干个无效等价类(从不同角度违反规则)。 - 细分等价类
在确知已划分的等价类中各元素在程序中的处理方式不同的情况下,则应再将该等价类进一步划分为更小的等价类,并建立等价类表。
黑盒测试
边界值分析法
概要
对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。
为什么使用边界值分析法
无数的测试实践表明,大量的故障往往发生在输入定义域或输出值域的边界上,而不是在其内部。因此,针对各种边界情况设计测试用例,通常会取得很好的测试效果。
基本思想
- 故障往往出现在输入变量的边界值附近。
- 基于可靠性理论中称为“单故障”的假设,即有两个或两个以上故障同时出现而导致软件失效的情况很少,也就是说软件失效基本上是由单故障引起的。
怎样用边界值分析法设计测试用例
- 首先确定边界情况。通常输入或输出等价类的边界就是应该着重测试的边界情况。
- 选取正好等于、刚刚大于或刚刚小于边界的值作为测试数据,而不是选取等价类中的典型值或任意值。
标准性(一般性)测试
对于一个含有 n 个变量的程序,保留其中一个变量,让其余的变量取正常值,被保留的变量依次取 min、min+、nom、max-、max 值,对每个变量都重复进行。这样,对于一个有 n 个变量的程序,边界值分析测试程序会产生 4n+1 个测试用例。
健壮性测试
- 边界值分析测试的一种扩展,除了取 5 个边界值外,还需要考虑采用一个略超过最大值(max+)及略小于最小值(min-)的取值,检查超过极限值时系统的情况
- 健壮性测试最有意义的部分不是输入,而是预期的输出
- 对于一个含有 n 个变量的程序,保留其中一个变量,让其余的变量取正常值,被保留的变量依次取 min、min+、min-,nom、max-、max, max+值,对每个变量都重复进行。这样,对于一个有 n 个变量的程序,边界值分析测试程序会产生 6n+1 个测试用例。
原则
- 如果输入条件规定了值的范围,则应取刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值作为测试输入数据。
- 如果输入条件规定了值的个数,则用最大个数,最小个数,比最小个数少一,比最大个数多一的数作为测试数据。
- 将规则 1)和 2)应用于输出条件,即设计测试用例使输出值达到边界值及其左右的值。
- 如果程序的规格说明给出的输入域或输出域是有序集合,则应选取集合的第一个元素和最后一个元素作为测试用例。
- 如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构的边界上的值作为测试用例。
- 分析规格说明,找出其它可能的边界条件。
最坏情况思想
边界值分析采用可靠性理论中的单缺陷假设,如果不考虑这种假设,那么应该关心当多个变量取极值时会出现什么情况。
- 最坏情况测试比边界值分析测试以及健壮性测试要彻底,边界值分析测试用例是最坏情况测试用例的真子集。
- N 变量函数的最坏情况测试,会产生5n个测试用例,而边界值分析只产生4n+1个测试用例
决策表法
概念
决策表是分析和表达多逻辑条件下执行不同操作情况的工具。
针对不同逻辑条件的组合值,分别执行不同的操作。决策表很适合于处理这类问题。
优点
- 能够将复杂的问题按照各种可能的情况全部列举出来,简明并避免遗漏。因此,利用决策表能够设计出完整的测试用例集合。
- 最为严格,最具逻辑性的测试方法。
组成
决策表通常由以下 4 部分组成:
- 条件桩—列出问题的所有条件
- 条件项—针对条件桩给出的条件列出所有可能的取值
- 动作桩—列出问题规定的可能采取的操作
- 动作项—指出在条件项的各组取值情况下应采取的动作
将任何一个条件组合的特定取值及相应要执行的动作称为一条规则。在决策表中贯穿条件项和动作项的一列就是一条规则。
决策表的生成
将任何一个条件组合的特定取值及相应要执行的动作称为一条规则,在决策表中贯穿条件项和动作项的一列就是一条规则。
构造决策表的 5 个步骤
- 列出所有的条件桩和动作桩。
- 确定规则的个数。
- 填入条件项。
- 填入动作项,得到初始决策表。
- 简化决策表,合并相似规则。
若表中有两条以上规则具有相同的动作,并且在条件项之间存在极为相似的关系,便可以合并。
合并后的条件项用符号“-”表示,说明执行的动作与该条件的取值无关,称为无关条件。
简化
简化是以合并相似规则为目标;
若表中有两条以上规则具有相同的动作,并且在条件项之间存在极为相似的关系,便可以合并。
优点
- 决策表最突出的优点是,能够将复杂的问题按照各种可能的情况全部列举出来,简明并避免遗漏。
- 利用决策表能够设计出完整的测试用例集合。
- 运用决策表设计测试用例可以将条件理解为输入,将动作理解为输出
适用范围
- if-else 逻辑突出;
- 输入变量之间存在逻辑关系;
- 涉及输入变量子集的计算;
- 输入与输出之间存在因果关系;
适用于使用决策表设计测试用例
- 规格说明以决策表形式给出,或较容易转换为决策表;
- 条件的排列顺序不会也不应影响执行的操作;
- 规则的排列顺序不会也不应影响执行的操作;
- 当某一规则的条件已经满足,并确定要执行的操作后,不必检验别的规则;
- 如果某一规则的条件要执行多个操作任务,这些操作的执行顺序无关紧要。
因果图法
产生的背景
没有考虑输入条件的各种组合、输入条件之间的相互制约关系的测试方法。多个输入条件组合起来可能出错的情况被忽视。
果图法设计测试用例思想
- 首先从程序规格说明书的描述中,找出因(输入条件)和果(输出结果或者程序状态的改变),
- 然后通过因果图转换为判定表,最后为判定表中的每一列设计一个测试用例.
定义
是一种利用图解法分析输入的各种组合情况,从而设计测试用例的方法,它适合于检查程序输入条件的各种组合情况。
基本符号
通常在因果图中用Ci 表示原因,用Ei 表示结果,各结点表示状态,可取值“0”或“1”。“0”表示某状态不出现,“1”表示某状态出现。
- 恒等:若 c1 是 1,则 e1 也为 1,否则 e1 为 0;
- 非:若 c1 是 1,则 e1 为 0,否则 e1 为 1;用符号“~”表示。
- 或:若 c1 或 c2 或 c3 是 1,则 e1 是 1,否则 e1 为 0,“或”可有任意个输入;用符号“V”表示。
- 与:若 c1 和 c2 都是 1,则 e1 为 1,否则 e1 为 0,“与”也可有任意个输入。用符号“∧”表示。
在实际问题当中输入状态相互之间还可能存在某些依赖关系,称为“约束”
- E 约束(异):a 和 b 中最多有一个可能为 1,即 a 和 b 不能同时为 1;
- I 约束(或):a、b、c 中至少有一个必须是 1,即 a、b、c 不能同时为 0;
- O 约束(唯一):a 和 b 必须有一个且仅有一个为 1;
- R 约束(要求):a 是 1 时,b 必须是 1;
- M 约束(强制):若结果 a 是 1,则结果 b 强制为 0。
因果图法设计测试用例步骤
- 分析程序规格说明书描述的语义内容,找出“原因”和“结果”,将其表示成连接各个原因与各个结果的“因果图”。
- 由于语法或环境限制,有些原因与原因之间或与结果之间的组合情况不能出现,用记号标明约束或限制条件;
- 将因果图转换成决策表;
- 根据决策表中每一列设计测试用例
优点
- 考虑到了输入情况的各种组合以及各个输入情况之间的相互制约关系。
- 能够帮助测试人员按照一定的步骤,高效率的开发测试用例。
- 因果图法是将自然语言规格说明转化成形式语言规格说明的一种严格的方法,可以指出规格说明存在的不完整性和二义性。
场景法
概念
基本流
按照正确的事件流实现的一条正确流程
备选流
出现故障或缺陷的过程,备选流就可以是从基本流来的,或是由备选流中引出的。
场景法设计测试用例的步骤
- 根据说明,描述出程序的基本流及各项备选流.
- 根据基本流和各项备选流生成不同的场景.
- 对每一个场景生成相应的测试用例.
- 对生成的所有测试用例重新复审,去掉多余的测试用例,测试用例确定后,对每一个测试用例确定测试数据值.
确定测试用例
- 对于每一个场景都需要确定测试用例。可以采用矩阵或决策表来确定和管理测试用例。
- 对于每个测试用例,存在一个测试用例 ID、条件(或说明)、测试用例中涉及的所有数据元素(作为输入或已经存在于数据库中)以及预期结果。
正交试验法
利用因果图来设计测试用例时,作为输入条件的原因与输出结果之间的因果关系,有时很难从软件需求规格说明中得到.往往因果关系非常庞大,导致利用因果图而得到的测试用例数目多得惊人,给软件测试带来沉重的负担.为了有效的,合理地减少测试的工时与费用,可利用正交试验法进行测试用例的设计
设计方法
- 正交试验设计方法是从大量的试验数据中挑选适量的、有代表性的点,从而合理地安排测试的一种科学的试验设计方法
- 使用已经造好了的表格——正交表来安排试验并进行数据分析。
概念
- 因子(因素)
在一项试验中,凡欲考察的变量称为因素(变量) - 水平(位级)
在试验范围内,因素被考察的值称为水平(变量的取值) - 什么是正交试验设计
是研究多因素多水平的一种设计方法,它是根据正交性从全面试验中挑选出部分有代表性的点进行试验,这些有代表性的点具备了 “均匀分散,齐整可比 ”的特点,正交试验设计是一种基于正交表的、高效率、快速、经济的试验设计方法。
构成
- 行数 (Runs):正交表中的行的个数,即试验的次数。行数计算公式
∑ ( 每个因素可取值数 − 1 ) + 1 \sum(每个因素可取值数-1)+1 ∑(每个因素可取值数−1)+1 - 因素数 (Factors):正交表中列的个数。
- 水平数 (Levels):任何单个因素能够取得的值的最大个数。正交表中包含的值为从 0 到“水平数-1”或从 1 到“水平数 ”
- 正交表的表示形式: L 行数 ( 水平 数 因素数 ) L_{行数}(水平数^{因素数}) L行数(水平数因素数)
正交表的正交性
- 整齐可比性
在同一张正交表中,每个因素的每个水平出现的 次数完全相同。由于在试验中每个因素的每 个水平与其它因素的每个水平参与试验的机率是 完全相同的,这就保证在各个水平中最大程度的 排除了其它因素水平的干扰 - 均衡分散性
在同一张正交表中,任意两个因素的水 平搭配(横向形成的数字对)是完全相同的。这 样就保证了试验条件均衡地分散在因素水平的完 全组合中。
正交表设计测试用例的步骤
- 有哪些因素(变量)
- 每个因素有哪几个水平(变量的取值)
- 选择一个合适的正交表
- 把变量的值映射到表中
- 把每一行的各因素水平组合做为一个测试用例
- 加上你认为可疑且没有在表中出现的组合
如何选择正交表
- 考虑因素(变量)的个数
- 考虑因素水平(变量的取值)的个数
- 考虑正交表的行数
- 取行数最少的一个
白盒测试
定义
又称为 结构测试 或 逻辑驱动测试
白盒测试主要是检查程序的内部结构、逻辑、循环和路径
白盒测试将被测程序看作一个打开的盒子,测试者能够看到被测源程序,可以分析被测程序的内部结构,此时测试的焦点集中在根据其内部结构设计测试用例。
遵循的原则
- 保证一个模块中的所有独立路径至少被测试一次。
- 所有逻辑值均需测试真 (true) 和假 (false) 两种情况。
- 检查程序的内部数据结构,保证其结构的有效性。
- 在上下边界及可操作范围内运行所有循环。
方法
常用测试用例设计方法有:
- 逻辑覆盖法(逻辑驱动测试)
- 基本路径法
- 循环测试法
- 程序插桩法
逻辑覆盖法
主要是测试覆盖率,以程序内在逻辑结构为基础的测试。包括以下 6 种类型:
语句覆盖
设计若干个测试用例,运行被测程序,使得每一可执行语句至少执行一次。
语句覆盖率:已执行的可执行语句占程序中可执行语句总数的百分比
- 复杂的程序不可能达到语句的完全覆盖
- 语句覆盖率越高越好
优点
- 检查所有语句
- 结构简单的代码的测试效果较好
- 容易实现自动测试
- 代码覆盖率高
- 如果是程序块覆盖,则不涉及程序块中的源代码
缺点
- 仅针对程序逻辑中显式存在的语句
- 在多分支的逻辑运算中无法全面的考虑。语句覆盖是最弱的逻辑覆盖。
语句覆盖不能检查出的错误
- 逻辑运算(&&、||)错误
- 循环语句错误
判定覆盖
设计若干个测试用例,运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次。又称为分支覆盖。
优点
判定覆盖具有比语句覆盖更强的测试能力。同样判定覆盖也具有和语句覆盖一样的简单性,无须细分每个判定就可以得到测试用例。
缺点
大部分的判定语句是由多个逻辑条件组合而成,若仅判断其整个最终结果,而忽略每个条件的取值情况,必然会遗漏部分测试路径。
判定覆盖仍是弱的逻辑覆盖。
条件覆盖
设计若干个测试用例,运行被测程序,使得程序中每个判断的每个条件的可能取值至少执行一次。
优点
增加了对条件判定情况的测试,增加了测试路径。
缺点
条件覆盖不一定包含判定覆盖。条件覆盖只能保证每个条件至少有一次为真,而不考虑所有的判定结果。
判定-条件覆盖
设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判定的可能结果也至少出现一次。
优点
能同时满足判定、条件两种覆盖标准。
缺点
判定/条件覆盖准则的缺点是未考虑条件的组合情况。
条件组合覆盖
设计足够的测试用例,运行被测程序,使得每个判断的所有可能的条件取值组合至少执行一次。
优点
条件组合覆盖准则满足判定覆盖、条件覆盖和判定/条件覆盖准则。
缺点
线性地增加了测试用例的数量。
路径覆盖
设计足够的测试用例,覆盖程序中所有可能的路径。
满足路径覆盖的测试用例并不一定满足组合覆盖。
实际测试中,即使对于路径数很有限的程序已经做到路径覆盖,仍然不能保证被测试程序的正确性,还需要采用其他测试方法进行补充。
基本路径测试
- 从一个程序的入口开始,执行所经历的各个语句的完整过程。
从广义的角度讲,任何有关路径分析的测试都可以被称为路径测试。
路径测试的理想情况是做到路径覆盖,但对于复杂性大的程序要做到所有路径覆盖是不可能的,在不能做到所有路径覆盖的前提下,如果某一程序的每一个独立路径都被测试过,那么可以认为程序中的每个语句都已经检验过了,即达到了语句覆盖。
方法
在控制流图的基础上,通过分析控制结构的环形复杂度,导出执行路径的基本集,再从该基本集设计测试用例。
4 个步骤:
- 画出程序的控制流图。
- 计算程序的环形复杂度,导出程序基本路径集中的独立路径条数,这是确定程序中每个可执行语句至少执行一次所必须的测试用例数目的上界。
- 导出基本路径集,确定程序的独立路径。
- 根据(3)中的独立路径,设计测试用例的输入数据和预期输出。
控制流图
对程序流程图的简化。
在选择或多分支结构中,分支的汇聚处应有一个汇聚节点。
边和结点圈定的区域叫做区域,当对区域计数时,图形外的区域也应记为一个区域。
控制流图中只有两种图形符号:
- 节点:以标有编号的圆圈表示,程序流程图中以下元素都要转化为节点。
- 程序流程图中矩形框所表示的处理
- 菱形表示的两个甚至多个出口判断
- 多条流线相交的汇合点
- 控制流线或弧:以箭头表示。
- 与程序流程图中的流线一致,表明了控制的顺序
- 控制流线通常标有名字
如果判断中的条件表达式是由一个或多个逻辑运算符 (OR, AND, …) 连接的复合条件表达式,则需改为 一系列只有单个条件的嵌套的判断。
环形复杂度
McCabe 复杂性度量,在进行程序的基本路径测试时,从程序的环路复杂性可导出程序基本路径集合中的独立路径条数。
程序的环路复杂性给出了程序基本路径集中的独立路径条数,这是确保程序中每个可执行语句至少执行一次所必需的测试用例数目的上界。
独立路径:指包括一组以前没有处理的语句或条件的一条路径。
从控制流图来看,一条独立路径是至少包含有一条在其它独立路径中从未有过的边的路径。一条路径这里指一条“程序通路”。
给定流图 G 的环形复杂度 V(G),计算方法(三种):
- 控制流图中区域的数量对应于环形复杂度
- V(G)=E-N+2, E 是流图中边的数量,N 是流图中节点的数量。即边-点+2
- V(G)=P+1, P 是流图 G 中的判定节点数。即判定节点+1。
导出测试用例
- 确保基本路径集中的每一条路径的执行。
- 根据判断结点给出的条件,选择适当的数据以保证某一条路径可以被测试到(用逻辑覆盖方法)。
- 每个测试用例执行之后,与预期结果进行比较。如果所有测试用例都执行完毕,则可以确信程序中所有的可执行语句至少被执行了一次。
必须注意,一些独立的路径,往往不是完全孤立的,有时它是程序正常的控制流的一部分,这时,这些路径的测试可以是另一条路径测试的一部分。
循环测试
4 种类型
- 简单循环
- 嵌套循环
- 连锁循环(串接循环)
- 非结构循环(不规则循环)
简单循环测试
- 零次循环:从循环入口到出口
- 一次循环:检查循环初始值
- 二次循环:两次通过循环
- m 次循环: 检查多次循环
- 最大次数循环 n、比最大次数多一次 n+1、少一次的循环 n-1。
嵌套循环测试
- 对最内层循环做简单循环的全部测试。所有其它层的循环变量置为最小值;
- 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环变量取最小值,所有其它嵌套内层循环的循环变量取“典型”值。
- 反复进行,直到所有各层循环测试完毕。
- 对全部各层循环同时取最小循环次数,或者同时取最大循环次数。
连锁循环
如果各个循环互相独立,则可以用与简单循环相同的方法进行测试。但如果几个循环不是互相独立的,则需要使用嵌套循环测试的办法来处理。
非结构循环
这一类循环应该使用结构化程序设计方法重新设计测试用例。
Z 路径覆盖
实现完全的路径覆盖是不可能的。舍掉一些次要因素,对循环机制进行简化,从而极大地减少路径的数量,使得覆盖这些有限的路径成为可能。这种简化意义下的路径覆盖称为 Z 路径覆盖
循环化简是指限制循环的次数。无论循环的形式和实际执行循环体的次数是多少,只考虑循环一次和零次两种情况。即:只考虑执行时进入循环体一次和跳过循环体的两种情况。
程序插桩技术
了解一个程序在某次运行中所有可执行语句被覆盖的情况,或是每个语句实际执行次数,最好的办法就是利用程序插桩技术.借助往被测程序中插入操作,来实现测试目的的方法。如最简单的插桩:在程序中插入打印语句printf(“…”)
语句
测试方法设计
设计插桩程序时需要考虑的问题包括:
- 需要探测哪些信息
- 在程序的什么部位设置探测点
- 需要设置多少个探测点
前两个问题需要结合具体的问题解决,并不能给出笼统的回答。至于第三个问题,需要考虑如何设置最少的探测点.
静态测试法
不实际运行软件,只是检查和审阅,主要对软件的编程格式,结构等方面进行评估。
代码审查
程序员和测试员组成的审查小组通过阅读、讨论和争议,对程序进行静态分析的过程。
步骤
- 小组成员提前阅读设计规格书、程序文本等相关文档
- 召开程序审查会,开发人员读程序,审查小组讨论、发现、解决问题
内容
- 检查代码和设计的一致性
- 检查代码对标准的遵循、可读性
- 检查代码的逻辑表达的正确性
- 检查代码结构的合理性
- 形成公司积累变成容易出错的 checklist
代码走查
程序员和测试员组成的审查小组通过逻辑运行程序,发现问题。
步骤
- 小组成员提前阅读设计规格书、程序文本等相关文档
- 利用测试用例,使程序逻辑运行,记录程序的踪迹,发现、讨论、解决问题
桌面检查
可视为由单人进行的代码检查或代码走查。
是程序员对源程序代码进行分析、检验,并补充相关的文档,发现程序中的错误的一种方法。
白盒测试综合策略
- 在测试中,应尽量先用工具进行静态结构分析。
- 测试中可采取先静态后动态的组合方式:先进行静态结构分析、代码检查和静态质量度量,再进行覆盖率测试。
- 利用静态分析的结果作为引导,通过代码检查和动态测试的方式对静态分析结果进行进一步的确认,使测试工作更为有效。
- 覆盖率测试是白盒测试的重点,一般可使用基本路径测试法达到语句覆盖标准;对于软件的重点模块,应使用多种覆盖率标准衡量代码的覆盖率;
- 在不同的测试阶段,测试的侧重点不同
在单元测试阶段,以代码检查、逻辑覆盖为主;
在集成测试阶段,需要增加静态结构分析、静态质量度量;
在系统测试阶段,应根据黑盒测试的结果,采取相应的白盒测试。
单元测试
业界标杆
单元测试(25%)
审查评审(20%)
设计(17%)
编码(14%)
需求(7%)
系统测试(4%)
计划和跟踪(4%)
发布后缺陷 0.06 Defects/KLOC
单元测试发现的缺陷密度:31 defects/KLOC
业界平均水平
商业软件
单元测试工作量/总工作量= 8.3%
编码工作量/总工作量= 16.6%
军工软件
单元测试工作量/总工作量= 10.1%
编码工作量/总工作量= 18.1%
高质量的程序取决于以下几个方面:
- 高质量的设计
- 规范的编码
- 有效的测试
编码阶段引入的缺陷远远多于其它阶段。
系统测试发现的缺陷大多数是编码缺陷。
测试版本频繁,测试和项目进度被无休止的拖延。
单元测试优点
- 最高的成本收益比
- 减少联调和后续测试的时间
- BUG 更容易定位
- 更有信心去修改老代码
单元测试概念
对单个的软件单元或者一组相关的软件单元所进行的测试,是代码级的测试。
单元是什么(测试对象)
(IEEE)软件单元
指软件设计说明中一个可独立测试的元素,是程序中一个逻辑上独立的部分,它不能再分解为其他软件成分。
(实践中)软件单元
指软件源代码中单个的函数,源文件或类。
总的来说单元就是:函数,源代码文件,类
单元测试原则
- 应该尽早地进行软件单元测试。
- 应该保证单元测试的可重复性。
- 尽可能地采用测试自动化的手段来支持单元测试活动。
单元测试内容
- 单元功能测试
- 单元接口测试
- 单元局部数据结构测试
- 单元中重要的执行路径测试
- 单元的各类错误处理路径测试
- 单元边界条件测试
单元测试由谁完成
单元测试可以是开发者本人执行,也可以是独立的专业测试人员执行。两者各有优势。
建议开发人员必须完整地做单元测
试,同时测试人员针对重点模块实施
独立的单元测试。
单元测试过程
包括 8 个活动:
- 确定单元测试计划
- 确定待测特性
- 制订单元测试规程
- 设计测试套件
- 构建测试套件
- 执行测试套件
- 检查终止条件
- 评估测试结果
单元测试计划
确定单元测试范围
- 尽可能争取完全地覆盖(原则上应该做到完全覆盖)
参考:通常以下情况必须安排单元测试:
1. 新模块
2. 新增代码比例超过20%
3. 核心模块 - 单元测试充分性要求
例如:语句行覆盖率=100%;分支覆盖率〉85%
测试覆盖率要求是测试充分性的一个方面,除此之外,在单元测试中还应考虑每个软件特性的测试覆盖,如函数性能。
-
终止条件
确定单元测试过程的正常终止条件。该终止条件应该包括了对测试充分性要求的满足。
识别可能造成单元测试过程异常终止的条件(如发现重大的设计错误、到达进度期限等)。 -
单元测试资源
估算进行测试活动所需的资源。应考虑测试人员、硬件、通信或系统软件、测试工具和其它资源。识别需要进行准备或申请的资源(如定制的测试工具),并做出相应的安排。 -
指明总体进度计划
基于资源和项目计划等方面的要求,确定单元测试活动的总体进度计划。
确定待测特性
- 研究待测特性要从研究单元的需求开始
功能需求、非功能需求(如性能或设计约束等)、与待测单元相关的任何使用或操作过程 - 单元的状态识别(针对状态机测试)
- 单元的数据特性识别(单元的输入输出数据分析)
以上研究分析对于制定单元测试方案和指导测试用例的设计很重要
待测特性分析过程中还有可能发现单元需求上的缺陷。
制定单元测试规程
- 输入
单元测试计划、待测特性分析结果、项目总体进度计划。 - 识别可重用技术(待查)
通过待测特性分析,可从用例库中识别出可以重用的测试用例和测试规程,以减少重复工作。 - 资源
详细列举单元测试所需资源,包括人员、设备、工具、环境等。 - 进度计划
详细的进度计划,包括风险分析和应对措施。 - 规程评审
设计测试套件
- 测试数据的准备(测试用例、脚本、驱动、桩、测试数据)
- 测试工具的开发/调试
- 构建测试环境
单元测试数据
单元测试设计中,测试数据的设计是很关键的,同样的测试规程,不
同的测试数据,可能会达到不同的测试结果。
- 正常数据:
在测试中所用的正常数据的量是最大的,而且也是最关
键的。少量的测试数据不能完全覆盖需求,但我们要从中提取出一
些具有高度代表性的数据作为测试数据,以减少测试时间。 - 边缘数据:
边缘测试是界于正常数据和错误数据之间的一种数据。
它可以针对某一种编程语言、编程环境或特定的数据库而专门设
定。边缘数据要靠测试人员的丰富经验来制定。 - 错误数据:
显而易见,错误数据就是编写与程序输入规范不符的数据从而检测输入筛选、错误处理等程序的分支。
执行测试套件
- 运行测试
- 确定测试结果,处理测试过程中的异常对每个测试用例,确定单元是否通过测试。对异常进行分析,并根据情况处理:
- 测试用例或测试数据的问题。修正并重新运行。
- 测试规程执行的问题。重新运行。
- 测试环境的问题。纠正测试环境并重新运行;或者异常终止测试,并汇报记录异常终止原因。
- 单元实现中的故障。纠正单元的故障,并运行所有的测试;或者异常终止测试,并汇报记录异常终止原因。
- 单元设计中的故障。纠正单元设计和实现中的故障,必要时修改测试设计和测试数据,并重新运行所有的测试。
检查终止条件
- 测试充分性检查
检查是否达到覆盖率要求,包括测试用例执行、通过覆盖率、被测单元代码、分支覆盖率。以及其它测试充分性要求。 - 异常终止条件检查
- 补充测试套件
以上条件不满足时,则需要补充测试套件,继续进行测试。
评估测试结果
- 按照单元测试报告模块出具单元测试报告
- 如有必要对单元测试报告进行评审
- 将所有测试相关工作产品纳入配置管理
常见单元测试的难点与对策
-
没有时间做单元测试
- 单元测试计划在项目计划应该有体现。
- 编写代码之前或同时,先设计测试用例。
-
单元测试责任人不清楚
- 强调单元测试必须由类包的设计者负责编写,因为只有这样,测试才能保证对象的运行时态行为符合需求。
- 让测试人员或第三方人员编写测试用例,将花费更多的工作量。
- 执行测试用例可以让测试人员或自动构造系统。
-
测试代码难以管理
- 采用测试工具管理测试代码,如 XUnit、C++Test、RTRT
- 配置管理中建立配置项。如不同模块的一组代码,建立相应测试代码目录和配置项
-
覆盖率难以手工统计
利用各种工具
- PureCoverage (C/C++/Java/.Net,Windows/UNIX)
- RTRT(C/C++/Java/Ada,嵌入式系统)
- C++Test(C/C++,Windows/UNIX)
- Discover(Delphi,Windows)
-
故障报告形式
- 各种工具一般都会生成测试报告
- XUnit 测试用例执行报告
- RTRT、C++Test 各种综合报告(测试用例执行结果、测
- 试用例覆盖率、内存检查和性能)
-
驱动和桩编写困难(可测试性差)
主要由以下方面导致:
- 被测试对象需要传入的参数过多。
- 内部的逻辑判断过多(内部牵扯复杂)。
- 和界面显示部分交互过于频繁(耦合性太强)。
- 被测对象过多的调用了其他类或方法。
- 需要构造的作为参数的对象本身过于复杂
解决:提高可测试性
- 坚持测试驱动设计(测试先于设计)。
优先编写测试代码。这是标准的 XP 方法。这不是说您应该一次性编写全部测试代码后,再一次性全部实现。对一些单元接口,编写一些测试代码,实现它们,再编写一些测试代码,再实现它们等等是个更好的办法。设计以这种方式得以进展;在实现阶段捕捉错误并在下一组测试中改正它。 - 功能分解
类:把功能分解到细粒度,提倡小类。
方法:尽量做到每个操作对应一个方法,使方法小型化。
功能分解促进:提高重用性,降低耦合度 - 分层原则
对于显示部分(GUI),尽量做到显示与控制分离。把代码移到 GUI 视图的外面。然后各种 GUI 动作就能成了模型上的简单方法调用。这样,对 GUI 测试者来说,通过方法调用测试功能比间接地测试功能容易的多。另一个好处是它使修改程序功能而不影响视图变的更容易。 - 抽象
我们可以想出各种各样的办法来降低耦合程度,但是归纳
起来,不外乎增加抽象的层次来隔离不同的类,这个抽
象层次可以是具体的类,也可以是接口。
GOF的23种设计模式,没有一种模式的思路不是从增加抽
象层次入手来解决问题的 - 接口
对于可能要作为参数的复杂类,可以做一个接口,用接
口说明外部程序组件使得我们可以容易地在测试案例中
模拟这些组件。当需要时可以实现按接口生成一个模拟
类作为参数传入。特别是当该类还没有完全实现时,这
种方法最为行之有效。 - 如果自己不负责测试工作,作为开发员在设计过程中要时刻提醒自己“我如何才能测试这些代码?我如何才能以可测试方式编写这些代码”。
- 重构是提高可测试性的主要手段
单元测试经验
- 测试驱动开发,开发以测试为导向
- 写不出测试用例,就谈不上编写单元代码
- 单元测试的执行要自动化。
- 单元测试的工作产品纳入配置管理。
开发一个单元的代码的步骤:
- 设计和编写测试它的用例代码
- 运行自动测试,检查是否发生错误
- 编写单元的代码
- 使用前面的用例回归测试它
集成测试
在单元测试和系统测试间起到承上启下的作用。
既能发现大量单元测试阶段不易发现的接口类错误,又可以保证在进入系统测试前及早发现错误,减少损失。
对系统而言,接口错误是最常见的错误。
单元测试通常是单人执行,而集成测试通常是多人执行或第三方执行。集成测试通过模块间的交互作用和不同。
人的理解和交流,更容易发现实现上、理解上的不一致和差错。
概念
集成测试又称“组装测试”、“联合测试”。集
成测试遵循特定的策略和步骤将已经通过单元测
试的各个软件单元(或模块)逐步组合在一起进
行测试,以期望通过测试发现各软件单元接口之
间存在的问题。
测试对象
理论上凡是两个单元(如函数单元)的组合测试
都可以叫做集成测试。实际操作中,通常集成测
试的对象为模块级的集成和子系统间的集成,其
中子系统集成测试称为组件测试。
测试周期始末
- 在开始体系结构设计的时候开始制定测试方案;
- 在进入详细设计之前完成集成测试方案;
- 在进入系统测试之前结束集成测试。
测试人员
- 集成测试可以在开发部进行,也可以由独立的测试部执行。
- 开发部尽量进行集成测试,测试部有选择地进行集成测试。
集成测试原则
- 集成测试是产品研发中的重要工作,需要为其分配足够的资源和时间。
- 集成测试需要经过严密的计划,并严格按计划执行。
- 应采取增量式的分步集成方式,逐步进行软件部件的集成和测试。
- 应重视测试自动化技术的引入与应用,不断提高集成测试效率。
- 应该注意测试用例的积累和管理,方便进行回归并进行测试用例补充。
关注点
- 穿越接口的数据是否会丢失
- 一个模块的功能是否会对另一个模块的功能产生不利影响
- 实现子功能的模块组合起来是否能够达到预期的总体功能
- 全局数据结构的测试
- 共享资源访问的测试
- 单个模块的误差经过集成的累加效应
主要内容
- 集成功能测试
- 接口测试
- 全局数据结构测试
- 资源测试
- 任务优先级冲突测试
- 性能和稳定性测试
集成单元实现的功能,集成后的功能(合一),考察多个模块间的协作,既要满足集成后实现的复杂功能,也不能衍生出不需要的多余功能(错误功能)。
主要关注:
模块间的接口包括函数接口和消息接口。
- 对函数接口的测试,应关注函数接口参数的类型和个数的一致性、输入/输出属性的一致性、范围的一致性。
- 对消息接口的测试,应关注收发双方对消息参数的定义是否一致、消息和消息队列长度是否满足设计要求、消息的完整性如何、消息的内存是否在发送过程中被非法释放、有无对消息队列阻塞进行处理等。
全局数据结构往往存在被非法修改的隐患
主要关注:
资源测试包括共享资源测试和资源极限测试。共享资源测试常应用于数据库测试和支撑的测试。
主要关注:
- 是否存在死锁现象;
- 是否存在过度利用情况;
- 是否存在对共享资源的破坏性操作;
- 公共资源访问锁机制是否完善。
资源极限测试关注系统资源的极限使用情况以及软件对资源耗尽时的处理,保证软件系统在资源耗尽的情况下不会出现系统崩溃。
某个部件的性能指标,及时发现性能瓶颈。
主要关注:
主要关注:
- 是否存在内存泄漏而导致长期运行资源耗竭;
- 长期运行后是否出现性能的明显下降;
- 长期运行是否出现任务挂起
方法
非递增式集成测试
所有软件模块单元测试后一次集成。
优点:测试过程中基本不需要设计开发测试工具。
缺点:对于复杂系统,当出现问题时故障定位困难,和系统测试接近,难以体现和发挥集成测试的优势。
递增式集成测试
逐渐集成,由小到大,边集成边测试,测完一部分,再连接一部分。在复杂系统中,划分的软件单元较多,通常是不会一次集成的。软件集成的精细度取决于集成策略。通常的做法是先模块间的集成,再部件间的集成。
优点:测试层次清晰,出现问题能够快速定位。
缺点:需要开发测试驱动和桩。
过程
- 集成测试策略制定
集成方法、内容、范围、通过准则;
工具考虑,复用分析;
基于项目人力、设备、技术、市场要求等各方面决策。 - 集成测试进度计划
工作量估算、资源需求、进度安排、风险分析和应对措施。 - 集成测试方案编制
接口分析、测试项、测试特性分析。体现测试策略。
- 测试规程/测试用例的设计和开发
- 确定的测试步骤、测试数据设计
- 测试工具、测试驱动和桩的开发
集成的基本策略比较多,分类比较复杂,但是都可以归结为以下两类:
非增量式集成策略(一步到位)
指在测试对象分析基础上,描述软件模块集成的方式、方法。
应当确定关键模块,对这些关键模块及早进行测试。
关键模块的特征:
- 完成需求规格说明中的关键功能;
- 在程序的模块结构中位于较高的层次(高层控制模块);
- 较复杂、较易发生错误;
- 有明确定义的性能要求。
优点:
- 方法简单
- 允许多测试人员同时并行工作,人力物力资源利用率较高
缺点:
- 必须为每个模块准备相应的驱动模块和桩模块,测试成本较高
- 一旦集成后包含多种错误,难以纠正。
增量式集成策略(逐步实现)
逐次将未曾集成测试的模块和已经集成测试的模块(或子系统)结合成程序包,再将这些模块集成为较大系统,在集成的过程中边连接边测试,以发现连接过程中产生的问题。
按照不同的实施次序,增量式集成测试又可以分为三种不同的方法:
自顶向下增量式测试
表示逐步集成和逐步测试是按照结构图自上而下进行的,即模块集成的顺序是首先集成主控模块(主程序),然后依照控制层次结构向下进行集成。从属于主控模块的按深度优先方式(纵向)或者广度优先方式(横向)集成到结构中去。
由3个步骤完成:
- 主控模块作为测试驱动器。
- 根据集成的方式(深度或广度),下层的桩模块一次一次地被替换为真正的模块。
- 在每个模块被集成时,都必须进行单元测试。重复第2步,直到整个系统被测试完成。
优点:
- 较早地验证了主要控制和判断点;
- 按深度优先可以首先实现和验证一个完整的软件功能;
- 功能较早证实,带来信心;
- 只需一个驱动,减少驱动器开发的费用;
- 支持故障隔离。
缺点:
- 桩的开发量大;
- 底层验证被推迟;
- 底层组件测试不充分。
适用范围:
一个维护型或被测试系统较小的项目
自底向上增量式测试
自底向上进行集成和测试时,需要为所测模块或子系统编制相应的驱动模块。
由3个步骤完成:
- 起始于模块依赖关系树的底层叶子模块,也可以把两个或多个叶子模块合并到一起进行测试
- 使用驱动模块对步骤1选定的模块(或模块组)进行测试
- 用实际模块代替驱动模块,与它已测试的直属子模块组装成一个更大的模块进行测试,重复上面的行为,直到系统最顶层模块被加入到已测系统中
优点:
- 对底层组件行为较早验证;
- 工作最初可以并行集成,比自顶向下效率高;
- 减少了桩的工作量;
- 能较好锁定软件故障所在位置。
缺点:
- 驱动的开发工作量大;
- 对高层的验证被推迟,设计上的错误不能被及时发现。
适用范围:
- 适应于底层接口比较稳定;
- 高层接口变化比较频繁;
- 底层组件较早被完成。
三明治增量式测试(混合增量式测试)
每日构造
意义
- 使平行编码的众多程序员定期同步到产品发布的主线上来是开发过程健康状况的脉搏,是进度监控的基础是连接开发、测试和程序经理的重要纽带
- 将彼此依赖的产品组件和部门连接到产品发布的主线上来提供理论上随时可以发布的版本,为重大产品决策提供宝贵的灵活性
关键
- 每天进行创建
- 每天进行冒烟测试
- 冒烟测试随着产品的增长而增长
- 每日构造发现的问题作为最高优先级的任务
- 在压力下不要放弃这个过程
过程
- 开发人员提交代码
- 编码规范检查
- 自动编译和链接
- 冒烟测试
系统测试
性能测试
验收测试
功能测试