7.1编码
编码就是把软件设计结果翻译成用某种设计语言书写的程序。
调试的目的:通过软件测试发现错误后,诊断并改正错误,由程序的编写者负责调试。
7.1.1选择程序设计语言
理想标准
- 有理想化的模块机制
- 可读性好的控制结构和数据结构
- 语言特点尽可能多的发现程序中的错误
- 有良好的独立编译机制
7.1.2编码风格
1.程序内部的文档
- 包括恰当的标识符
- 适当的注解
- 程序的视觉组织
2.数据说明
- 次序标准化(例如按照数据结构或数据类型确定说明次序)
- 多个变量名按字母顺序排列
- 用注解说明使用程序设计语言实现复杂的数据结构的方法和特点
3.语句构造
语句应该简单而直接,不能为了提高效率而让程序复杂
下述规则有助于语句简单明了
- 不要为了节省空间把多个语句写在一行
- 尽量避免复杂的条件测试
- 尽量减少对“非”条件的测试
- 避免大量使用循环嵌套和条件嵌套
- 利用括号让表达式的运算次序清晰直观
4.输入输出
- 对所有输人数据都进行检验。
- 检查输入项重要组合的合法性。
- 保持输入格式简单。
- 使用数据结束标记,不要要求用户指定数据的数目。
- 明确提示交互式输入的请求,详细说明可用的选择或边界数值。
- 当程序设计语言对格式有严格要求时,应保持输入格式一致。
- 设计良好的输出报表。
- 给所有输出数据加标志。
5.效率
效率主要指处理机时间和存储器容量两个方面。
- 效率是性能要求
- 效率是靠好设计来提高的。
- 程序的效率和程序的简单程度是一致的,不要牺牲程序的清晰性和可读性来不必要地提高效率。
根据着三个方面应该考虑效率问题
- 程序运行时间
- 存储器效率
- 输入输出效率”
7.2软件测试基础
7.2.1软件测试的目标
- 测试的定义:为了发现程序中的错误而执行程序的过程
- 测试的目标:尽可能多地发现并排除软件中潜藏的错误
- 好的测试方案是极可能发现论今为止尚未发现的错误的测试方案。
- 成功的测试是发现了至今为止尚未发现的错误的测试。
- 测试只能查找出程序中的错误,不能证明程序中没有错误。
7.2.2软件测试的准则
- 所有测试都应该能追溯到用户需求。
- 应该远在测试开始之前就制定出测试计划。
- 把 Pareto原理应用到软件测试中。(Pareto原理说明,测试发现的错误中的80%很可能是由程序中20%的模块造成的。)
- 应该从“小规模”测试开始,并逐步进行“大规模”测试。
- 穷举测试是不可能的。
- 为了达到最佳的测试效果,应该由独立的第三方从事测试工作。
7.2.3测试方法
- 黑盒测试:把程序看作一个黑盒子,完全不考虑程序的内部结构和处理过程。黑盒测试是在程序接口进行的测试,它只检査程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据并产生正确的输出信息,程序运行过程中能否保持外部信息(例如数据库或文件)的完整性。黑盒测试又称为功能测试
- 白盒测试:前提是可以把程序看成装在一个透明的白盒子里,测试者完全知道程序的结构和处理算法。这种方法按照程序内部的逻辑测试程序,检测程序中的主要执行通路是否都能按预定要求正确工作。白盒测试又称为结构测试
7.2.4测试步骤
技术测试的阶段:①单元测试 ②集成测试 ③系统测试 ④验收测试
1.模块测试
步骤:每个模块作为一个单独的实体来测试,发现的往往是编码和详细设计的错误
目的:保证每个模块作为一个单元能正确运行,所以模块测试通常又称为单元测试。
2.子系统测试
步骤:把经过单元测试的模块放在一起形成一个子系统来测试。模块相互间的协调和通信是这个测试过程中的主要问题,这个步骤着重测试模块的接口
3.系统测试
步骤:把经过测试的子系统装配成一个完整的系统来测试。发现的往往是软件设计中的错误,也可能发现需求说明中的错误
目标:不仅应该发现设计和编码的错误,还应该验证系统确实能提供需求说明书中指定的功能,而且系统的动态特性也符合预定要求。
不论是子系统测试还是系统测试,都兼有检测和组装两重含义,通常称为集成测试。
4.验收测试
步骤:把软件系统作为单一的实体进行测试,在用户积极参与下进行,主要使用实际数据(系统将来要处理的信息)进行测试。
目的:是验证系统确实能够满足用户的需要,在这个测试步骤中发现的往往是系统需求说明书中的错误。
验收测试也称为确认测试
5.平行运行
步骤:新开发的系统和旧开放的系统同时运行,比较两个系统的处理结果
目的:
- 可以在准生产环境中运行新系统而又不冒风险。
- 用户能有一段熟悉新系统的时间。
- 可以验证用户指南和使用手册之类的文档。
- 能够以准生产模式对新系统进行全负荷测试,可以用测试结果验证性能指标。
7.2.5测试阶段的信息流
(1)软件配置
需求说明书、设计说明书和源程序清单
(2)测试配置
测试计划
测试方案:测试时使用的输入数据(称为测试用例),每组输入数据预定要检验的功能,每组输入数据预期应该得到的正确输出。
对测试结果分析应该考虑两种可能:
- 软件的可靠性是可以接受的(软件可靠性模型使用错误率估计将来出现的错误的情况)
- 进行的测试尚不足以发现严重的错误
7.3单元测试
单元测试集中检测软件设计的最小单元——模块。
单元测试和编码属于软件过程的同一个阶段。主要使用白盒测试技术,多个模块测试可以并行地进行。
两种不同类型的测试方法:人工测试和计算机测试。完成单元测试工作,这两种测试方法各有所长,互相补充,缺少任何一种都会使查找效率降低。
7.3.1测试重点
1.模块接口
首先对模块接口数据流测试,若数据不能正确进出,则其他所以测试都是不切实际的。
进行模块接口测试主要检查以下几个方面
- 参数的数目、次序、属性或者单位系统与变元是否统一
- 是否修改了知输入用的变元
- 全局变量的定义和用法在各个模块中是否统一
2.局部数据结构
主要检查局部数据说明、初始化、默认值等方面的错误
3.重要执行通路
穷举测试是不可能的,应该选择最有代表性,最可能发现错误的执行通路。
4.出错处理通路
处理错误的通路。
5.边界条件
边界测试是单元测试中最后也可能是最重要的任务。软件尝尝在边界上失效。
7.3.2代码审查
审查小组进行人工测试源程序,称为代码审查。
审查小组成员应有下述4让组成
- 组长,应该是一个很有能力的程序员,而且没有直接参与这项工程。
- 程序的设计者。
- 程序的编写者。
- 程序的测试者。
若一个人既是程序的设计者又是编写者,或既是编写者又是测试者,则审查小组中应该再增加一个程序员
审查小组的任务:发现错误而不是改正错误。
7.3.3计算机测试
模块并不是一个独立的程序,因此必须为每个单元测试开发驱动软件和(或)存根软件。
驱动程序也就是一个“主程序”,它接收测试数据,把这些数据传送给被测试的模块,并且印出有关的结果。
存根程序也可以称为“虚拟子程序”。存根程序代替被测试的模块所调用的模块。
驱动程序和存根程序代表开销,为了减少开销使用渐增式测试方法,详见7.4集成测试
7.4集成测试
集成测试是测试和组装软件的系统化技术,主要目标是发现与接口有关的问题。
由模块组装成程序时有两种方法。
- 非渐增式测试方法:先分别测试每个模块,再把所有模块按设计要求放在一起结合成所要的程序;
- 渐增式测试方法:把下一个要测试的模块同已经测试好的那此模块结合都来进行测试,测试完以后再把下一个应该测试的模块结合进来测试。这种方法实际上完成了单元测试和集成测试。
由于非渐增式测试一下子把所有模块放在一起作为一个整体测试,测试者面对的情况十分复杂。因此,目前在进行集成测试时普遍采用渐增式测试方法。
当使用渐增方式把模块结合到程序中去时,有自顶向下和自底向上两种集成策略。
7.4.1自顶向下集成
从主控制模块开始,沿着程序的控制层次向下移动,逐渐把模块结合。可以选择深度优先或者宽度优先策略,见下图7.3
深度优先策略:选取左通路,首先结合模块 M1 、M2和M5;其次,M8或M6(如果为了使M2具有适当功能需要M6)将被结合进来。然后构造中央的和右侧的控制通路。
宽度优先策略:首先结合模块 M2、M3和M4(代替存根程序S),然后结合下一个控制层次中的模块 M5、M6和M7;如此继续下去,直到所有模块都被结合起来。
优点:不需要测试驱动程序,能够在早期验证系统的主要功能。
缺点:需要存根程序(模拟下层模块可能会出现错误,使测试困难),低层关键模块中的错误较晚发现。
7.4.2自底向上集成
自底向上测试从“原子”模块(即在软件结构最低层的模块)开始组装和测试。因为是
从底部向上结合模块,总能得到所需的下层模块处理功能,所以不需要存根程序。
用下述步骤可以实现自底向上的结合策略。
① 把低层模块组合成实现某个特定的软件子功能的族。
②写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出。
③ 对由模块组成的子功能族进行测试。
④ 去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的能族。
上述第②~④步实质上构成了一个循环。图 7.4描绘了自底向上的结合过程。把模块组合成族1、族 2和族3,使用驱动程序(图中用虚线方框表示)对每个子功能行测试。族1和族2中的模块附属于模块 Mₐ,去掉驱动程序D₁和D₂,把这两个族同Mₐ连接起来。类似地,在和模块 Mb结合之前去掉族3的驱动程序D₃。最终Mₐ和Mb这两个模块都与Mc结合起来。
7.4.3不同集成策略的比较
自顶向下的优点:不需要测试驱动程序,能够早期实现并验证系统的主要功能,而且能在早期发现上层模块的接口错误。
自顶向下缺点:是需要存根程序(模拟下层模块可能会出现错误,增加测试困难),可能遇到与此相联系的测试困难,低层关键模块中的错误发现较晚。
自底向上的优缺点与自顶向下的优缺点相反。
在测试实际的软件系统时,应该根据软件的特点以及工程进度安排,选用适当的测试策略。一般说来,纯粹自顶向下或纯粹自底向上的策略可能都不实用,人们在实践中创造出许多混合策略。
7.4.4回归测试
回归测试是回归测试就是用于保证由于调试或其他原因引起的变化,不会出现额外错误的测试活动。
回归测试集(已执行过的测试用例的子集)包括下述3类不同的测试用例。
- 检测软件全部功能的代表性测试用例。
- 专门针对可能受修改影响的软件功能的附加测试。
- 针对被修改过的软件成分的测试。
回归测试重新执行每个功能的全部测试用例,是低效而且不切实际的。
7.5确认测试
确认测试的定义:为了保证软件确实满足了用户需求而进行的一系列活动。确认测试也称为验收测试。
目的:验证软件的有效性。
验证是指保证软件正确地实现某个特定要求的一系列活动。
有效性的定义:如果软件的功能和性能如同用户所合理期待的那样,软件就是有效的。
7.5.1确认测试的范围
确认测试必须有用户积极参与,或者以用户为主进行。
确认测试通常使用黑盒测试法。
设计测试计划和测试过程
测试计划包括要进行的测试种类和进度安排,测试过程规定了用来检测软件是否与需求一致的测试方案。
确认测试有下述两种可能结果
- 功能和性能与用户要求一致,软件可以接受。
- 功能和性能与用户要求有差距
在这个阶段发现的问题往往和需求分析阶段(需求分析阶段描述了用户对软件的合理期望)的差错有关。
7.5.2软件配置复查
确认测试的一个重要内容是复查软件配置。
复查的目的:保证软件配置到所有成分都齐全,质量符合要求,文档与程序完全一致。
除人工审查软件配置之外,还应该遵守用户指南及其他操作程序,以便检查使用手册的完整性和正确性。
7.5.3Alpha和Beta测试
如果一个软件是为多个客户开发的,让每个客户都进行正式的验收测试是不现实的。这种情况下,绝大多数软件开发商都使用Alpha测试和Beta测试。
Alpha测试是指用户在开发环境下进行,并且在开发者的“指导”下进行测试。
开发者负责记录发现的错误和使用中遇到到错误。总之,Alpha测试是在受控的环境中进行的。
Beta测试是指用户在一个或多个用户实际使用的环境下进行的,开发者通常不在测试现场。
用户记录在测试过程中遇到的一切问题,定期的把问题报告给开发者。
Beta测试是软件在开发者不能控制的环境中的“真实”应用。
7.6白盒测试技术
7.6.1逻辑覆盖
逻辑覆盖是对一系列测试过程的总称。
1.语句覆盖
含义:选择足够多的测试数据,使被测程序中每个语句至少执行一次。
例如:图7.5
为了使每个语句都执行一次,程序执行的路径应该是sacbed,只需要输入下面的测试数据
A=2 ,B=0 ,X=4
实际上X可以是任意实数。
语句覆盖对程序的覆盖很少,上面的例子中两个判定条件都只测试了条件为真的情况,如果条件为假时处理有错误,显然不能发现。可以看出,语句覆盖是很弱的逻辑覆盖标准。
2.判定覆盖
判定覆盖又叫分支覆盖。含义:不仅每个语句必须至少执行一次,而且每个判定的每种可能的结果都应该至少执行一次,也就是每个判定的每个分支都至少执行一次。
满足判定覆盖标准可以选择sacbed和sabd的两组测试数据,或者sacbd和sabed的两组测试数据。
例如下面两组测试数据:
①A=3,B=0,X=3(覆盖sacbd)
②A=2,B=1,X=1(覆盖sabed)
判定覆盖比语句覆盖强,但是对程序到覆盖程序仍然不高,例如,上面的测试数据只覆盖了程序全部路径的一半。
3.条件覆盖
含义:不仅每个语句至少执行一次,而且使判定表达式中的每个条件都取到各种可能的结果。
图7.5中共有两个判定表达式,每个表达式中有两个条件覆盖,应该选取测试数据使得在a点有下述结果:
A>1,A≤1,B=0,B≠0
在b点有下述结果出现:
A=2,A≠2,X>1,X≤1
只需要使用下面两组测试数据:
①A=2,B=0,X=4(满足A>1,B=0,A=2和X>1,执行路径sacbed)
②A=1,B=1,X=1(满足A≤1,B≠0,A≠2和X≤1,执行路径sabd)
条件覆盖通常比判定覆盖强,但是也有相反的情况:虽然每个条件都取到了不同的结果,判定表达式却始终只取一个值。例如使用下面两组测试数据,则只满足条件覆盖标准不满足判定覆盖标准。(第二个判定表达式都值总为真)
① A=2, B=0,X=1(满足 A>1, B=0, A=2和X≤1的条件,执行路径 sacbed)
② A=1, B=1, X=2(满足 A≤1, B≠0, A≠2和X>1的条件,执行路径 sabed)
4.判定/条件覆盖
既然判定覆盖不一定包含条件覆盖,条件覆盖也不一定包含判定覆盖,自然会提出一种能同时满足这两种覆盖标准的逻辑覆盖,这就是判定/条件覆盖。
含义:每个判定表达式中的每个条件都取到各种可能的值,而且每个判定表达式也都取到各种可能的结果。
例如图7.5,下述两组测试数据满足判定/条件覆盖的标准:
①A=2,B=0,X=4
②A=1,B=1,X=1
但是,这两组测试数据也就是为了满足条件覆盖标准最初选取的两组数据,因此,有时判定/条件覆盖也并不比条件覆盖强。
5.条件组合覆盖
条件组合覆盖是更强的逻辑覆盖标准。
含义:每个判定表达式中条件的各种可能组合都至少出现一次。
图7.5,共有8种可能的条件组合,分别是:
(1)A>1,B=0
(2)A>1,B≠0
(3)A≤1,B=0
(4)A≤1,B≠0
(5)A=2, X>1
(6)A=2,X≤1
(7)A≠2,X>1
(8)A≠2,X≤1
和其他逻辑覆盖标准中的测试数据一样,条件组合(5)~(8)中的X值是指在程序程图第二个判定框(b点)的X值。
下面的4组测试数据可以使上面列出的8种条件组合每种至少出现一次:
①A=2,B=0,X=4 (针对(1)和(5)两种组合,执行路径sacbed)
②A=2,B=1,X=1 (针对(2)和(6)两种组合,执行路径sabed)
③A=1,B=0,X=2(针对(3)和(7)两种组,执行路径 sabed)
④A=1,B=1,X=1 (针对(4)和(8)两种组合,执行路径sabd)
显然,满足条件组合覆盖标准的测试数据,也一定满足判定覆盖,条件覆盖和判定/条件覆盖标准。
因此,条件组合覆盖是前述几种覆盖标准中最强的。但是满足条件组合覆盖标准的测试数据并不一定能让程序中每条路径都执行到,例如,上述4组测试数据都没有测试到路径sacbd。
从对程序路径的覆盖程度分析,可以提出下述一些主要的覆盖标准。
6.点覆盖
定义:如果连通图G的子图G¹是连通的,而且包含了G的所有节点,则称G¹是G的点覆盖。
在正常情况下流图是连通的有向图。满足点覆盖标准要求选取足够多的测试数据,使得程序执行路径至少经过流图的每个结点一次,由于流图的每个结点与一条或多条语句相对应,显然,点覆盖标准和语句覆盖标准是相同的。
7.边覆盖
定义:如果连通图G的子图G¹¹是连通的,而且包含G的所有边,则称G¹¹是G的边覆盖。为了满足边覆盖的测试标准,要求选取足够多的测试数据,使得程序执行路径至少经过流图中每条边一次。 通常边覆盖和判定覆盖是一致的。
8.路径覆盖
含义:程序的每条可能的路径都至少执行一次。(如果程序图中有环,则要求每个环至少经过一次。)
7.6.2控制结构测试
1.基本路径测试
①基本路径测试
②计算流图的环形复杂度。
环形复杂度决定了独立路径的数量
③确定线性独立路径的基本集合。
独立路径是指至少引入程序的一个新处理语句集合或一个新条件的路径。
④设计可强制执行基本集合中每条路径的测试用例。
某些独立路径不能以独立的方式测试,也就是说,程序的正常流程不能形成独立执行该路径所需的数据组合,在这种情况下,这些路径必须作为另一个路径的一部分来测试。
2.条件测试
优点:①容易度量条件的测试覆盖率 ②程序内条件的测试覆盖率可指导附加测试的设计
目的:检测程序中条件的错误和其他的错误。
3.循环测试
目的:专注于测试循环结构的有效性
通常有以下三种循环:
- 简单循环
- 串接循环
- 嵌套循环
7.7黑盒测试技术
黑盒测试着重测试软件功能。黑盒测试不能取代白盒测试,它是与白盒测试互补的测试方法。
黑盒测试发现的错误的类型:
- 功能不正确或遗漏了功能
- 界面错误
- 数据结构错误或外部数据库访问的错误
- 性能错误
- 初始化和终止错误
白盒测试在测试过程早期阶段进行,而黑盒测试用于测试过程的后期。
7.7.1等价划分
定义:把程序的输入域划分成若干个等价类,从每个等价类中选取几个代表性的数据。
目的:减少测试用例,提高测试效率。
划分等价类的规则:
- 规定了输入值的范围,则可以划分出一个有效等价类(输入值在范围内),两个无效等价类(输入值小于最小值或大于最大值)。
- 如果规定了输入数据的个数,则类似地也可以划分出一个有效等价类和两个无效等价类
- 如果规定了输入数据的一组值,而且程序对不同输入值做不同处理,则每个允许的输入值是一个有效的等价类,此外还有一个无效的等价类(任一个不允许的输入值)。
- 如果规定了输入数据必须遵循的规则,则可以划分出一个有效的等价类(符合规则)若干个无效的等价类(从各种不同角度违反规则)。
- 如果规定了输入数据为整型,则可以划分出正整数、零和负整数3个有效类。
- 如果程序的处理对象是表格,则应该使用空表,以及含一项或多项的表。
测试方案应该覆盖所有有效等价类和无效等价类。
7.7.2边界值分析
处理边界情况时程序最容易发生错误。
按照边界值分析法,应该选取刚好等于、刚刚小于和刚刚大于边界值的数据作为测试数据。
通常设计测试方案时总是联合使用等价划分和边界值分析两种技术。
7.7.3错误推测
基本思想:列举程序中可能有的错误和发生错误的特殊情况,并且根据它们选择测试方案。
在一段程序中已经发现的错误数目往往和尚未发现的错误数成正比。
等价划分法和边界值分析法都只孤立地考虑各个输入数据的测试功效,而没有考虑多个输入数据的组合效应,可能会遗漏了输入数据易于出错的组合情况。
选择输入组合的一个有效途径是利用判定表或判定树为工具,列出输入数据各种组合与程序应作的动作(及相应的输出结果)之间的对应关系,然后为判定表的每一列至少设计一个测试用例。
选择输入组合的另一个有效途径是把计算机测试和人工检查代码结合起来。例如:通过代码检查发现程序中的两个模块使用并修改某些共享的变量,如果一个模块对这些变量的修改不正确,则会引起另一个模块出错。
7.8调试
调试(也称为纠错)作为成功测试的后果出现。也就是说,调试是在测试发现错误之后排除错误的过程。
7.8.1调试过程
调试不是测试,但是它总是发生在测试之后。如图7.8调试过程从执行一个测试用例开始,评估测试结果,如果发现实际结果和预期结果不一致,则这种不一致就是一个症状,它表明在软件中存在着隐藏的问题。调试过程试图找出产生症状的原因,以便改正错误。
调试过程会有以下两种结果之一:
①找到了问题的原因并把问题改正和排除掉了;
②没找出问题的原因。在这种情况下,调试人员可以猜想一个原因,并设计测试用列来验证这个假设,重复此过程直至找到原因并改正了错误。
软件错误的下述特征也是相当重要的原因:
(1)症状和产生症状的原因可能在程序中相距甚远,也就是说,症状可能出现在程序的一个部分,而实际的原因可能在与之相距很远的另二部分。紧耦合的程序结构更加剧了这种情况。
(2)当改正了另一个错误之后,症状可能暂时消失了。
(3)症状可能实际上并不是由错误引起的(例如,舍人误差)。
(4) 症状可能是由不易跟踪的人为错误引起的。
(5) 症状可能是由定时问题而不是由处理问题引起的。
(6)可能很难重新产生完全一样的输入条件(例如,输入顺序不确定的实时应用系统)。
(7) 症状可能时有时无,这种情况在硬件和软件紧密地耦合在一起的嵌入式系统中特别常见。
(8)症状可能是由分布在许多任务中的原因引起的,这些任务运行在不同的处理机上。
7.8.2调试途径
1.蛮干法
蛮干法可能是寻找软件错误原因都最低效都方法,仅当所有其他方法豆失败了的情况下,才使用这种方法。
2.回溯法
回溯是一种相当常用的调试方法,当调试小程序时这种方法是有效的。
具体做法是,从发现症状的地方开始,人工沿程序的控制流往回追踪分析源程序代码,直到找出错误原因为止。但是随着程序规模的扩大,应该回溯的路径数目也变得越来越大,以至彻底回溯变成完全不可能了。
3.原因排除法
对分查找法、归纳法和演绎法都属于原因排除法
对分查找法的基本思路是,如果已经知道每个变量在程序内若干个关键点的正确值,则可以用赋值语句或输入语句在程序中点附近“注人”这些变量的正确值,然后运行程序并检查所得到的输出。如果输出结果是正确的,则错误原因在程序的前半部分;反之,错误原因在程序的后半部分。对错误原因所在的那部分再重复使用这个方法,直到把出错范围缩小到容易诊断的程度为止。
归纳法是从个别现象推断出一般性结论的思维方法。使用这种方法调试程序时,首先把和错误有关的数据组织起来进行分析,以便发现可能的错误原因。然后导出对错误原因的一个或多个假设,并利用已有的数据来证明或排除这些假设。当然,如果已有的数据尚不足以证明或排除这些假设,则需设计并执行一些新的测试用例,以获得更多的数据。
演绎法从一般原理或前提出发,经过排除和精化的过程推导出结论。采用这种方法调试程序时,首先设想出所有可能的出错原因,然后试图用测试来排除每一个假设的原因。如果测试表明某个假设的原因可能是真的原因,则对数据进行细化以准确定位错误。
一旦找出错误就必须改正它,但是改正一个错误可能会引入更多的错误,因此在动手改正错误之前应该考虑下述三个问题:
(1)是否同样的错误也在程序其他地方存在?在许多情况下,一个程序错误是由错误的逻辑思维模式造成的,而这种逻辑思维模式也可能用在别的地方。仔细分析这种逻辑模式,有可能发现其他错误。
(2)将要进行的修改可能会引入的“下一个错误”是什么?在改正错误之前应该仔细研究源程序(最好也研究设计文档),以评估逻辑和数据结构的耦合程度。如果所要做的修改位于程序的高耦合段中,则修改时必须特别小心谨慎。
(3)为防止今后出现类似的错误,应该做什么?如果不仅修改了软件产品还改进了开发软件产品的软件过程,则不仅排除了现有程序中的错误,还避免了今后在程序中可能出现的错误。
7.9软件可靠性
测试阶段的根本目标是消除错误,保证软件的可靠性。
7.9.1基本概念
1.软件可靠性的定义
定义:软件可靠性是程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。
在上述定义中包含的随机变量是时间间隔。显然,随着运行时间的增加,运行时出现程序故障的概率也将增加,即可靠性随着给定的时间间隔的加大而减少。
按照 IEEE的规定,术语“错误”的含义是由开发人员造成的软件差错(bug), 而术语“故障”的含义是由错误引起的软件的不正确行为。在下面的论述中,将按照 IEEE 规定的含义使用这两个术语。
2.软件的可用性
定义:软件可用性是程序在给定的时间点,按照规格说明书的规定,成功地运行的概率。
可靠性和可用性之间的主要差别是,可靠性意味着在0到t这段时间间隔内系统没有失效,而可用性只意味着在时刻t,系统是正常运行的。因此,如果在时刻上系统是可用的,则有下述种种可能:在0到上这段时间内,系统一直没失效(可靠);在这段时间内失效了一次,但是又修复了;在这段时间内失效了两次修复了两次;····…
如果在一段时间内,软件系统故障停机时间分别为tᵈ¹,tᵈ²…,正常运行时间分别为tᵛ1,tᵛ² …, 则系统的稳态可用性为:
如果引入系统平均无故障时间 MTTF和平均维修时间 MTTR的概念,则系统的稳态可用性为:
平均维修时间 MTTR是修复一个故障平均需要用的时间,它取决于维护人员的技术水平和对系统的熟悉程度,也和系统的可维护性有重要关系,第8章将讨论软件维护问题。平均无故障时间 MTTF 是系统按规格说明书规定成功地运行的平均时间,它主要取决于系统中潜伏的错误的数目,因此和测试的关系十分密切。
7.9.2估算平均无故障时间的方法。