总是很难忘记生活的点点滴滴, 脑海中总是闪过好多的曾经, 美好的回忆, 但成长中却让我们失去了很多, 很想在忙碌的生活中淡淡忘记; 不曾放低的东西却始终让我忘记不了, 但我还要在忙碌的生活中继续生活!

软件测试的艺术(精华)

上一篇 / 下一篇  2008-11-04 11:39:53 / 个人分类:测试知识

软件测试的艺术

软件测试:
一个过程或一系列过程,用来确定计算机代码完成了其应该完成的功能,不执行其不该有的操作。
软件应该是可预测的、稳定的。
第二章 软件测试的心理学和经济学
软件测试心理学:
测试是为了发现错误而执行程序的过程。
成功的”测试:测试某段程序时发现错误,且该错误可修复或者本次测试可以最终确定再无其他可查出的错误,则被称作“成功的”测试。
不成功的”测试:未能适当地对程序进行检查或未能找出错误的测试,被称为“不成功的”测试。
几种错误的观点:
软件测试就是证明软件不存在错误的过程。此为理想状况。
软件测试就是证明“软件做了其应该做的”过程。忽略的软件做了不该做的。
软件测试更适宜被视为试图发现程序中的错误的破坏性的过程。软件做了其应该做的,未做其不应该做的。
软件测试的经济学:
黑盒测试:称为数据驱动的测试或输入/输出驱动的测试。与程序的内部机制和结构完全无关。
重点:发现程序不按其规范正确运行的环境条件。
此方法若想发现所有的错误,判定标准:“穷举输入测试”。此不可能达到。有两方面含义:一是我们无法测试一个程序以确保它是无错的;二是要考虑软件测试的经济学问题。
白盒测试:逻辑驱动测试。允许检查程序的内部结构。
重点:将程序的每一条语句至少执行一次(穷举路径测试)。存在两个问题:
程序中不同逻辑路径的数量可能达到天文数字;
即使测试到程序中的所有路径,程序仍然可能存在着错误。此有三个原因:
1.不能保证程序符合其设计规范:如将升序排列错编成降序排列。
2.可能会因为缺少某些路径而存在的问题。
3.可能不会暴露数据敏感错误。
软件测试的原则:
1.测试用例中一个必须部分是对预期输出或结果进行定义;
 一个测试用例包括两个部分:
   对程序的输入数据的描述;
对程序在上述输入数据下的正确输出结果的精确描述。
 2.程序员应当避免测试自己编写的程序;
 3.编写软件的组织不应当测试自己编写的软件;
 4.应当彻底检查每一个测试的执行结果;
 5、测试用例的编写不仅应当根据有效和预料到的输入情况,而且也应当根据无效和未料到的输入情况;
 6.检查程序是否“未做其应该做的”和“做了其不应该做的”;
 7.应避免测试用例用后即弃,除非软件本身是一次性软件;
保留测试用例,当程序其他部件发生更动后重新执行,就是所谓的“回归测试”。在人机交互方式的测试下,尤其会忽略这一问题。
 8.计划测试工作时不应默许假定不会发现错误;
 9、程序某部分存在更多错误的可能性,与该部分已发现错误的数量成正比;
  测试总是倾向于聚集存在,故对容易存在错误的部分进行额外的测试。
 10.软件测试是一项极富创造性、极具智力挑战性的工作。
总结:三个重要的测试原则:
 软件测试是为发现错误而执行程序的过程。
 一个好的测试用例具有较高的发现某个尚未发现的错误的可能性。
 一个成功的测试用例能够发现某个尚未发现的错误。
第三章、代码检查、走查与评审
今天,并不是所有的软件测试人员都要阅读代码,但研读程序代码是测试工作的一部分。
“人工测试”:开始于代码编码之后,基于计算机的测试开始之前使用的方法。是查找错误方面非常有效。其原因:
 错误发现越早,改正成本越低;
 程序员改正基于计算机发现的错误所犯的失误,要比改正早期发现的问题所犯的失误要更多。代码检查与走查:
两种重要的人工测试方法。
他们的共同点:要求人们组成一个小组来阅读或直观检查特定的程序。“头脑风暴会”
优点:一旦发现错误,就能在代码中精确定位,降低调试成本。查出30%~70%的逻辑设计和编码错误。
缺点:往往只能发现一些“简单”的错误。
因此代码检查/走查与基于计算机的测试是互补关系。
代码检查:
  以组为单位阅读代码,它是一系列规程和错误检查技术的集合。重点:在规程、所要填写的表格。
  一个代码检查小组由四人组成:
一人发挥协调作用(称职的程序员):相当于质量控制工程师。无需对程序的细节很清楚;为代码检查分发材料,安排进程;在代码检查中起主导作用;记录发现的所有错误;确保所有错误随后得到改正。
代码检查有两项工作:
 程序编码人员逐条语句讲述程序的逻辑结构。
 对者历来常见的编码错误列表分析程序。
代码检查的优点:
 发现错误;
 程序员会得到编程风格、算法选择及编程技术等方面的反馈信息。
 早期发现程序中最易出错部分的方法之一。
用于代码检查的错误列表:
  代码检查过程的重要部分:就是对照一份错误列表,来检查程序是否存在常见的错误。
1. 数据引用错误
是否引用的变量未赋值或未初始化?
所有的数组引用,是否每一个下标的值都有相应维规定的界限之内?
所有的数组引用,是否每一个下标的值都是整数?
对所有的通过指针或引用变量的引用,当前引用的内存单元是否分配?即“虚调用”。
如果一个内存区域具有不同属性的别名,当通过别名进行引用时,内存区域中的数据值是否具有正确的属性?
变量的类型或属性是否与编译器所预期的一致?
在使用的计算机上,当内存分配的单元小于内存可寻址的单元大小时,是否存在直接或间接的寻址错误?
当使用指针或引用变量时,被引用的内存的属性是否与编译器所预期的一致?
假如一个数据结构在多个过程或子程序中被引用,那么每个过程或子程序对该结构的定义是否都相同?
如果字符串有索引,当对数组进行索引操作或下标引用,字符串的边界取值是否有“仅差一个”(off-by-one)的错误?
对于面向对象的语言,是否所有的继承需求都在实现类中得到了满足?
2. 数据声明错误
是否所有的变量都进行了明确的声明?
如果变量所有的属性在声明中没有明确说明,那么默认的属性能否被正确理解?
如果变量在声明语句中被初始化,那么它的初始化是否正确?
是否每个变量都被赋予了正确的长度和数据类型?
变量的初始化是否与其存储空间的类型一致?
是否存在着相似名称的变量?
3. 运算错误
是否存在不一致的数据类型的变量间运算?
是否有混合模式的运算?
是否有相同数据类型,不同字长变量间的运算?
赋值语句的目标变量的数据类型是否小于右边表达式的数据类型或结果?
在表达式的运算中是否存在表达式向上或向下溢出的情况?
除法运算中的除数是否可能为0?
如果计算机表达变量的基本方式是基于二进制的,那么运算结果是否不精确?
在特定场合,变量的值是否超过了有意义的范围?
对于包含一个以上操作符的表达式,赋值顺序和操作符的优先顺序是否正确?
整数的运算是否有使用不当的情况,尤其是除法?如:2*i/2==i ?
4. 比较错误
是否有不同数据类型的变量之间的比较运算?
是否有混合模式的比较运算,或不同长度的变量间的比较运算?
比较运算符是否正确?
每个布尔表达式所叙述的内容是否都正确?
布尔运算符的操作数是否是布尔类型的?比较运算符和布尔运算符是否错误地混在一起了?
在二进制的计算机上,是否有用二进制表示的小数或浮点数的比较运算?
对于那些包含一个以上布尔运算的表达式,赋值顺序以及运算符的优先顺序是否正确?
编译器计算布尔表达式的方式是否会对程序产生影响?
5. 控制流程错误
如果程序包含多条分支路径,索引变量的值是否会大于可能的分支数量?
是否所有的循环最终都终止了?
程序、模块或子程序是否最终都终止了?
由于实际情况没有满足循环的入口条件,循环体是否有可能从未执行过?
如果循环同时由迭代变量和一个布尔条件所控制,如果循环越界了,后果会如何?
是否存在“仅差一个”的错误,如迭代数量恰恰多一次或少一次?
如果编程语言中有语句组或代码块的概念,是否每一组语句都有一个明确的while语句,并且do语句也与其相应的语句组对应?
是否存在不能穷尽的判断?
6. 接口错误
被调用模块接收到的形参数量是否等于调用模块发送的实参数量?另外,顺序是否正确?
实参的属性是否与相应形参的属性相匹配?
实参的量纲是否与对应形参的量纲相匹配?
此模块传递给彼模块的实参数量,是否等于彼模块期望的形参数量?
此模块传递给彼模块的实参的属性,是否与彼模块相应形参的属性相匹配?
此模块传递给彼模块的实参的量纲,是否与彼模块相应形参的量纲相匹配?
如果调用了内置函数,实参的数量、属性、顺序是否正确?
如果一个模块或类有多个入口点,是否引用了与当前入口点无关的形参?
是否有子程序改变了某个原本仅为输入值的形参?
如果存在全局变量,在所有引用它们的模块中,它们的定义和属性是否相同?
常数是否以实参形式传递过?
7. 输入/ 输出错误
如果对文件明确声明过,其属性是否正确?
打开文件的语句中各项属性的设置是否正确?
格式规范是否与I/O语句中的信息相吻合?
是否有足够的可用内存空间,来保留程序将读取的文件?
是否所有的文件在使用之前都打开了?
是否所有的文件在使用之后都关闭了?
是否判断文件结束的条件,并正确处理?
对I/O出错情况处理是否正确?
任何打印或显示的文本信息中是否存在拼写或语法错误?
8. 其他检查
如果编译器建立了一个标识符交叉引用列表,那么对该列表进行检查,查看是否有变量从未引用过,或仅被引用过一次?
如果编译器建立了一个属性列表,那么对每一个变量的属性进行检查,确保没有赋予过不希望的默认属性值?
如果程序编译通过了,但计算机提供了一个或多个“警告”或“提示”信息,应对此逐一进行认真检查。“警告”:编译器对程序某些操作的正确性有所怀疑。“提示”:可能会列出没有声明的变量,或者是不利于代码优化的用法。
程序或模块是否具有足够的鲁棒性?即:它是否对其输入的合法性进行了检查?
程序是否遗漏了某一功能?
代码走查:
  代码检查和代码走查采用的错误检查技术不同,操作规程也有所不同。
  代码走查参与者:一个极富经验的程序员、一个程序设计语言专家、一个程序员新手、最终将维护程序的人员、一个来自其他不同项目的人员、一个来自该软件编程小组的程序员。
桌面检查:
  桌面检查是人工查找错误的第三种过程。由单人进行的代码检查或代码走查:由一个人阅读程序,对照错误列表检查程序,对程序推演测试数据。
  效率相当低。
同行评分:
  这种人工评审方法与程序测试并无关系,但与代码阅读的思想有关。
总结:
  开发人员通常不会考虑的一种测试形式:人工测试。主要方法:
   利用错误列表进行代码走查;
   小组代码走查;
   桌面检查;
   同行评审。
第四章、测试用例的设计
软件测试最重要的因素是设计和生成有效的测试用例。
由于时间和成本的约束,软件测试的最关键问题是:在所有可能的测试用例中,哪个子集最有可能发现最多的错误?
 随机输入测试:效率最低。
 黑盒测试方法:等价类划分、边界值分析、因果图分析、错误猜测
 白盒测试方法:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、多重条件覆盖。
实际工作中,推荐先使用黑盒测试方法,然后视情况需要使用白盒测试方法。
白盒测试:
  关注测试用例执行的程度或覆盖程序逻辑结构(源代码)的程度。
  逻辑覆盖测试:
   语句覆盖:有很大的不足,以至于它通常没有什么用处。
判定覆盖或分支覆盖:较强一些的逻辑覆盖准则。要求编写足够的测试用例,使得每一个判断都至少有一个为真或为假的输出结果。
判断覆盖通常可以满足语句覆盖。但有三种情况例外:
 程序中不存在判断。
 程序或子程序/方法有多重入口点。
 在ON单元里的语句。
条件覆盖:比判断覆盖更强的准则。要求编写足够的测试用例确保将一个判断中的每一条件的所有可能的结果至少执行一次。包括ON单元的每一个入口点都至少调用一次。条件覆盖会使判断中的各个条件都取到两个结果“真”和“假”。
判定/条件覆盖准则:要求设计出足够的测试用例,将一个判断中的每个条件的所有可能的结果至少执行一次,将每个判断的所有可能的结果至少执行一次,将每个入口点都至少调用一次。
 缺点:由于有些特定的条件会屏蔽掉其他条件,常常并不能全部都执行到。
多重条件覆盖:要求编写足够的测试用例,将每个判定中的所有可能的条件结果的组合,以及所有的入口点都至少执行一次。
显然,满足多重条件覆盖,就满足了判定覆盖或分支覆盖、条件覆盖、判定/条件覆盖准则。
而且,在存在循环的情况下,多重条件覆盖所需要的测试用例的数量通常会远远小于其路径的数量。
总之,对包含每个判断只存在一种条件的程序,最简单的测试准则就是设计出足够数量的测试用例:
1、 将每个判断的所有结果都至少执行一次;
2、 将所有的程序入口都至少调用一次;
而对于包含多重条件判断的程序,则要求设计足够的测试用例,将每个判断所有可能的条件结果组合,以及所有的入口点都至少执行一次。
等价划分
 精心挑选的测试用例应具备两个特性:
1、严格控制测试用例的增加,减少为达到“合理测试”的某些即定目标而必须设计的其他测试用例的数量。即:使用较少的测试用例,体现尽可能多的不同的输入情况。
2、它覆盖了大部分其他可能的测试用例。尽量将程序的输入范围进行划分,划分为有限数量的等价类。
 等价类划分方法设计测试用例有两个步骤:
1、 确定等价类:
有效等价类:代表对程序有效的输入;
无效等价类:代表其他任何可能的不正确的输入条件。
确定等价类的指导原则:
a. 如果输入条件规定了一个取值范围,则应确定一个有效等价类,两个无效等价类。
b. 如果输入条件规定取值的个数,那么应确定出一个有效等价类和两个无效等价类。
c. 如果输入条件规定一个输入值的集合,而且有理由认为程序会对每个值进行不同的处理,那么应确定一个有效等价类和一个无效等价类。
d. 如果樽俎输入条件规定了“必须是”的情况,那么应确定一个有效等价类和一个无效等价类。
2、 生成测试用例。
使用等价类来生成测试用例。其过程:
a、 为每个等价类设置一个不同的编号;
b、 编写新的测试用例,尽可能多地覆盖那些尚未被涵盖的有效等价类,直到所有的有效等价类都被测试用例所覆盖。
c、 编写新的用例,覆盖一个且仅一个尚未被涵盖的无效等价类,直到所有的无效等价类都被测试用例所覆盖。
边界值分析
 边界条件:是指输入和输出等价类中那些恰好处于边界、或超过边界、或在边界以下的状态。
 边界值分析方法与等价划分方法的不同点:
1、与从等价类中挑选出任意一个元素作为代表不同,边界值分析需要选择一个或多个元素,以便等价类的每一个边界都经过一次测试;
2、与仅仅关注输入条件不同,还需要考虑从结果空间设计测试用例。
 设计边界值分析方法的测试用例的一些通用指南:
1、如果输入条件规定了一个输入值范围,那么应针对范围的边界设计测试用例,针对刚刚越界的情况设计无效输入测试用例。
2、如果输入条件规定了 输入值的数量,那么应针对最小数量输入值、最多数量输入值、以及比最小数量少一个,比最大数量多一个的情况设计测试用例。
3、对每个输出条件应用指南1,还要注意检查结果空间的边界,因为输入的边界并不代表是输出的边界。
4、对每个输出条件应用指南2。
5、如果程序的输入或输出是一个有序序列,则应特别注意该序列的第一个和最后一个元素。
6、此外,还要发挥聪明才智找出其他的边界条件。
 边界值分析方法和等价划分之间的重要区别:边界值分析考察正处于等价划分边界或边界附近的状态。
 边界值分析方法常常使用得不好,因为边界条件可能非常微妙,常常很难确定。
因果图
 边界值分析和等价划分的一个弱点:未对输入条件的组合进行分析。
 因果图有助于用一个系统的方法选择出高效的测试用例集,而且还可以指出规格说明的不完整性和不明确之处。其生成测试用例的过程如下:
  1、将规格说明分解为可执行的片段。
2、确定规格说明中的因果关系。“因”:一个明确的输入条件或输入条件的等价类。“果”:指一个输出条件或系统转换。一旦因果关系被确定下来,每个“因”和“果”都被赋予一个唯一的编号。
3、分析规格说明的语义内容,并将其转换为连接因果关系的布尔图。
4、给图加上注解符合,说明由于语法或环境的限制而不能联系起来的“因”和“果”。
5、通过仔细地跟踪图中的状态变化情况,将因果图转换成一个有限项的判定表,表中的每一列代表一个测试用例。
6、将判定表指的列转换为测试用例。
 因果图方法是一个根据条件的组合而生成测试用例的系统性的方法。它确实能产生一组有效的测试用例,但通常它不能生成全部应该被确定的有效测试用例。而且也没有充分考虑边界条件。因果图可以考虑边界条件,但这会导致因果图急剧复杂化,因此最好单独考虑边界值分析。
 因果图方法难点在于:将因果图转化为判定表。
错误猜测
 错误猜测是一项依赖于直觉的非正规的过程,因此很难描述出这种方法的规程。其基本思想:列举出可能犯的错误或错误易发情况的清单,然后依据清单来编写测试用例,检查特定的输入值中有0,或特定的输出值被强制为0的情况。另一思想:在阅读规格说明时联系程序员可能做的假设来确定测试用例。
测试策略
 因为每一种测试方法可提供一组有用的测试用例,但不能单独提供一个完整的测试用例集,因此采用测试策略来合理组合。一组合理的策略如下:
  1、如果规格说明中包含输入条件组合的情况,应首先使用因果图分析法。
  2、在任何情况下都应该使用边界值分析方法。
  3、应为输入和输出确定有效和无效等价类。
  4、使用错误猜测技术增加更多的测试用例。
5、针对上述测试用例集检查程序的逻辑结构。使用判定覆盖、条件覆盖、判定/条件覆盖或多重条件覆盖准则。
 上述策略并不能保证可发现所有的错误,但实践证明是一个合理的折中方案。
第五章、模块(单元)测试
模块测试(单元测试):是对程序中的单个子程序、或过程进行测试的过程。
这样做有三个动机:
 1、它是一种管理组合的测试元素的手段。
 2、模块测试减轻了调试的难度,利于错误定位。
 3、模块测试通过为我们提供同时测试多个模块的可能,可将并行工程引入软件测试中。
模块测试的目的:将模块的功能与定义模块的规格说明或接口规格说明进行比较。测试是为了揭示出模块与其规格说明存在着矛盾。
模块测试包括三个方面:
 1、测试用例的设计方式。
 2、模块测试及集成的顺序。
 3、对执行模块测试的建议。
测试用例设计
   需要使用两种类型的信息:模块的规格说明、模块的源代码。
   模块的规格说明:规定了模块的输入和输出参数以及模块的功能。
   模块测试方法:面向白盒测试。
模块测试的测试用例设计过程:使用一种或多种白盒测试方法分析模块的逻辑结构,然后使用黑盒测试方法对照模块的规格说明以补充测试用例。
说明:多重条件覆盖准则要优于其他准则,任何逻辑覆盖准则尚不足以胜任作为生成模块测试用例的唯一手段。
增量测试
 在执行模块测试过程中,要考虑两点:
    1、如何设计一个有效的测试用例集。
    2、将模块组装成工作程序的方式。包括增量测试和非增量测试。
   两种增量测试策略:自顶向下的和自底向上的开发和测试过程。
非增量测试(崩溃(big-bang)测试):先独立地测试每个模块,然后再将这些模块组装成完整的程序进行测试。
增量测试(集成):先将下一步要测试的模块装到测试完成的模块集合中,然后再进行测试。
测试单独的模块需要一个特殊的驱动模块(driver module)和一个或多个桩模块(stub module)
结论:
  1、非增量测试工作量更多一些,而增量测试工作量少一些。
2、若使用增量测试,可较早地发现模块中与不匹配接口,不正确假设相关的编程错误。而非增量测试只能到最后的组装测试才能看到。
3、若使用增量测试,调试会更容易进行,更利于错误定位。
4、增量测试会将测试进行得更彻底。
5、非增量测试所占用的机器时间显得少一些。但需要更多的驱动模块和桩模块。
6、模块测试开始阶段,若使用非增量测试,会有更多的机会进行并行操作。
总之,增量测试相对更好一些。
自顶向下测试和与自底向上测试
 “自顶向下的测试”、“自顶向下的开发”、“自顶向下的设计”:前两者是同义词,但“自顶向下的设计”是一个独立的概念,其可采用自底向上的方式进行增量测试。
 自底向上的测试常常会被错误地当作非增量测试。
 自顶向下的测试:
  从程序的顶部或初始模块开始测试。
增量的唯一准则:要成为合乎条件的下一个模块,至少一个该模块的从属模块(调用它的模块)事先经过了测试。
要点:桩模块的编写、采用什么样的形式将测试用例提交给程序。
采用什么样的形式将测试用例提交给程序:测试数据是通过其一个或多个桩模块提交给模块的。如果被测模块仅仅调用桩模块一次,则需要编写多个桩模块版本,以此来提交多个测试用例。
增量的指南:
 1、如果程序中存在关键部分,则关键部分应尽早添加进去。
 2、在设计模块序列时,应将I/O模块尽可能早的添加进来。
自顶向下策略的缺陷:略。
 自底向上的测试:
  开始于程序中的终端模块。
增量的唯一准则:要成为合乎条件的下一个模块,该模块的所有从属模块(它调用的模块)事先经过了测试。
要点:驱动模块的编写。
驱动模块编写:包含有效的测试输入、调用被测模块且将输出显示出来的模块。无需编写多个版本。
增量的指南:
 如果程序中存在关键部分,则关键部分应尽早添加进去。
不足:没有早期程序框架的优点。
比较:
增量测试策略 优点 缺点
自顶向下 1、如果主要的缺陷发生在程序的顶层将非常有利。
2、一旦引入I/O功能,提交测试用例会更容易。
3、早期的程序框架可以进行演示,并可激发积极性。 1、必须开发桩模块。
2、桩模块要比最初表现的更复杂。
3、在引入I/O功能之前,向桩模块中引入测试用例比较困难。
4、创建测试环境可能很难,甚至无法实现。
5、观察测试输出很困难。
6、使人误解设计和测试可以交迭进行。
7、会导致特定模块测试的完成延后。
自底向上 1、如果主要的缺陷发生在程序的底层将非常有利。
2、测试环境比较容易建立。
3、观察测试输出比较容易。 1、必须开发驱动模块
2、直到最后一个模块添加进去,程序才形成一个整体。
执行测试
当测试用例造成模块输出的实际结果与预期结果不匹配的情况时,存在两个可能的解释:模块错误 or 预期的结果错误(测试用例不正确)。因此应对测试用例进行测试。
自动化测试工具:可以降低对驱动模块的需求;流程分析工具可以列举出程序中的路径,找出从未被执行的语句。
对预期输出进行定义是测试用例必不可少的部分。
执行测试时,应该查找程序的副作用(不该执行的操作)。
第六章、更高级别的测试
当程序无法实现其最终用户要求的合理功能时,就发生了一个软件错误。
  软件开发过程在很大程度上是沟通有关最终程序的信息、并将信息从一种形式转换到另一种形式。因此,绝大部分软件错误都可以归因为信息沟通和转换时发生的故障、差错和干扰。
  模块测试:发现程序模块与其接口规格说明之间的不一致。
  功能测试:为了证明程序未能符合其外部规格说明。
  系统测试:为了证明软件产品与其初始目标不一致。
  
集成测试:并不作为一个独立的测试步骤,而且在进行增量模块测试时,它是模块测试的隐含部分。
功能测试:
  一个试图发现程序与其外部规格说明之间存在不一致的过程。
  外部规格说明:一份从最终用户的角度对程序行为的精确描述。
  功能测试属黑盒测试。对外部规格说明进行分析以获取测试用例集。方法:等价类划分、边界值分析、因果图分析、错误猜测。
系统测试:
  最容易被错误理解,也是最困难的测试过程。并非是测试整个系统或程序功能的过程。而是将系统或程序与其初始目标进行比较。
  系统测试并不局限于系统,它是一个试图说明程序作为一个整体是如何不满足其目标的过程。如果产品没有一组书面的、可度量的目标,系统测试就无法进行。注意:不是与外部规格说明进行比较。
  系统测试有两个方面的作用:将程序与其目标和用户文档相比较、将用户文档与程序目标相比较。
能力测试:
   判断目标文档提及的每一项能力是否都确实已经实现。
  容量测试:
   为了证明程序不能处理目标文档中规定的数据容量。
  强度测试:
使程序承受高负载或强度的检验。即在很短的时间间隔内达到的数据或操作的数量峰值。
易用性测试:
  1、每个用户界面是否都根据最终用户的智力、教育背景和环境要求而进行了调整?
  2、程序的输出是否有意义、不模糊且没有计算机的杂乱信息?
  3、错误诊断是否直接,用户是否需要有一定的经历才能理解它?
4、整体的用户界面是否在语法、惯例、语义、格式、风格和缩写方面展现出了相当程度的概念完整性、基本的一致性和统一性?
  5、在准确性极为重要的环境里,输入中是否有足够的冗余信息?
  6、系统是否包含过多或不太可能用到的选项?
  7、对于所有的输入,系统是否返回了某些类型的即时确认信息?
  8、程序是否易于使用?
安全性测试:
  设计测试用例来突破程序安全检查的过程。
性能测试
  在特定负载和配置环境下程序的响应。
存储测试:
  设计测试用例来证明程序使用的内存和辅存的容量、以及临时文件或溢出文件的大小。
配置测试:
至少应该使用每一种类型的设备,以最大和最小的配置来测试程序。如软件本身的配置可忽略掉某些组件;运行在不同的操作系统、使用不同的Web浏览器等。
兼容性/配置/转换测试:
  证明兼容性目标未被满足,转换过程并未生效。尤其是数据库升级。
安装测试:
   是系统测试的一个重要部分。对自动安装系统而言,尤为重要。
   可靠性测试:
   测试软件或系统的平均故障间隔时间(MIBF)目标或合理的功能错误目标。
   可恢复性测试:
如操作系统、数据库管理系统和远程处理系统等软件通常都有可恢复性目标,说明系统如何从程序错误、硬件失效和数据错误中恢复过来。系统测试的目标就是证明这些恢复机制不能够发挥作用。使平均恢复时间(MTTR)最小。
适应性测试:
 软件可能有适应性或可维护性的目标。这些目标可能定义了系统提供的服务辅助功能,包括存储转存程序或诊断程序、调试明显问题的平均时间、维护过程以及内部业务文档的质量等。
文档测试:
 需要检查用户文档的正确性。
过程测试:
 必须对所有已规定的人工的操作过程进行测试。
系统测试的执行:
 系统测试的关键是决定由谁来进行测试。唯一一个不能由开发机构来执行的测试。
测试工作小组:几位系统测试专家、一位最终用户代表、一位人类工程学工程师、系统的设计者。
验收测试:
通常由程序的客户或最终用户来进行。不是软件开发机构的职责。将程序的实际操作与原始合同进行对照。验收测试最好的方法是设计测试用例,尽力证明程序没有满足合同的要求。
安装测试:
 为了发现在安装过程中出现的错误。
  1.用户必须选择大量的选项。
  2.必须分配并加载文件和库。
  3.必须进行有效的硬件配置。
  4.软件可能要求网络连通,以便与其他软件连接。
此外测试用例还要检查以确认已选的选项集合互不冲突。系统的所有部件全部存在,所有的文件已经创建并包含必须内容、硬件配置妥当等。
测试的计划与控制:
 一个良好的测试计划包括:
   目标:定义每个测试阶段的目标。
   结束准则:规定每个测试阶段何时可以结束。
   进度:每个阶段的时间表。
   责任:每个阶段,应确定谁设计、编写和验证测试用例,谁来修改发现的软件错误。
  测试用例库及标准:用于确定、编写以及存储测试用例的系统方法。
   工具:需使用的测试工具。
   计算机时间:计划每个测试阶段所需的计算机时间。
   硬件配置:如何满足需求及何时满足。
  集成:定义程序如何组装在一起的方法。即:系统集成计划。
跟踪步骤:跟踪测试进行中的方方面面,包括错误易发模块的定位,以及有关进度、资源和结束准则的进展评估。
调试步骤:制定上报已发现错误、跟踪错误修改进程以及将修改部分加入系统中去的机制。
回归测试:对程序作了功能改进或进行了修改后进行,其目的是判断程序的改动是否引起了程序其他方面的退步。通常重新执行测试用例中的某些子集。
测试结束准则:
软件测试过程中最难回答的一个问题。
最常见的两个准则:
 1、用完了安排的测试时间后,测试便结束。
 2、当执行完所有测试用例都未发现错误,测试便结束。
另外三类有用的准则:
 1、根据特定的测试用例设计技术。如:
测试用例来源于:满足多重条件覆盖准则、及对模块接口规格说明进行边界值分析,所有测试用例都不成功。
测试用例来源于:因果图分析、边界值分析、错误猜测,所有测试用例都不成功。
存在三个问题:对那些无特定方法测试阶段无效;要依赖于主观度量;不同于设置一个目标再让测试人员选择最佳的实现方法。
 2、以确切的数量来描述结束测试的条件。
强化了软件测试的定义。但存在两个问题:如何获得要发现的错误数量;利用以前程序的经验来预测出数字。
独立的测试机构:
 雇用独立的公司进行软件测试。
第七章、调试
调试有两个步骤:
   1、错误定位;
   2、修改错误。
  虽然调试必不可少,但不受程序员欢迎,其原因:
   1、个人自尊会从中阻扰。
   2、热情耗尽。
   3、可能会迷失方向。
   4、必须自力更生。
暴力法调试:
  可划分为三类:
   1、利用内存信息输出来调试。
    是最缺乏效率的调试,原因:
     难以在内存区域与源程序中的变量之间建立对应关系。
     即使对于复杂度较低的程序,内存信息输出会产生数量庞大的数据,大多与调试无关。
     内存信息输出显示的是程序的静态快照,无程序的动态状态。
内存信息输出很少可以精确地在错误发生的地方产生,无法显示错误发生时程序状态。
通过分析输出的内存信息来发现问题的方法并不太多。
   2、根据一般的“在程序中插入打印语句”建议来调试。
    比内存信息输出要好一些,但仍有很多缺点:
     不是鼓励我们去思考程序中的问题,而主要是一种碰运气的方法。
     它所产生的需要分析的数据量非常庞大。
它要求我们修改程序,这些修改可能会掩盖错误、改变关键的时序关系或引入新的错误。
它可能对小型程序有效。对大型程序,成本就相当高。而且对某些程序无法使用。
3、 使用自动化的调试工具进行调试。
其工作机制类似于在程序中插入打印语句,但并不修改程序本身。
调试工具可设断点。
暴力调试法的问题:忽略了思考过程。建议在其他的方法都失效的情况下,作为我们思考过程的补充,而不是替代方法。
归纳法调试:
 步骤:
  1、确定相关数据。主要错误是未能将所有可用的数据或症状都考虑进来。
  2、组织数据。
  3、作出假设。研究线索之间的关系,利用线索结构作出关于错误原因的假设。
  4、证明假设。
演绎法调试:
  步骤:
   1、列出所有可能的原因或假设。
   2、利用数据排除可能的原因。
   3、提炼剩下的假设。
   4、证明剩下的假设。
回溯法调试:
  在小型程序中定位错误的一种有效的方法是沿着程序的逻辑结构回溯不正确的结果,直到找出程序逻辑出错的位置。
测试法调试:
  使用测试用例来调试,目的是提供有用的信息,供定位某个被怀疑的错误之用。
  此处测试用例与供测试使用的测试用例存在区别:供测试的测试用例“胖”;而供调试的测试用例“瘦”。
调试的原则:
  定位错误的原则:
   1、动脑筋:对错误症状的有关信息进行分析。
   2、如果遇到了僵局,就留到稍后解决。
   3、如果遇到了困境,就把问题描述给其他人听。
   4、仅将测试工具作为第二种手段
   5、避免使用试验法-仅将其作为最后的手段。
  修改错误的技术:
   1、存在一个缺陷的地方,很可能还存在其他缺陷。
   2、应纠正错误本身,而不仅是其症状。
   3、正确纠正错误的可能性并非100%
   4、正确修改错误的可能性随着程序规模的增加而降低。
   5、应意识改正错误会引入新错误的可能性。
   6、修改错误的过程也是临时回到设计阶段的过程。
   7、应修改源代码,而不是目标代码。
错误分析
  调试可以告诉我们软件错误的本质,关于软件错误本质的信息可以为改进将来的设计、编码和测试过程提供有价值的反馈信息。
  错误出现在什么地方?需回溯研究程序文档和项目的历史。是最有价值的问题。
  谁制造了这个错误?
  哪些做得不正确?准确地判断出错误发生的原因。
  如何避免该错误的出现?
  为什么错误没有早些发现?
  该如何更早的发现错误?
第八章、极限测试
XP的开发方法的目的是在短时间内开发高质量的程序。XP模型高度依赖模块的单元和验收测试。这种形式的测试被称为极限测试(XT)。
极限编程基础
  XP与传统的开发过程相比有不同之处:
XP避免了大规模项目的综合症。XP的策划阶段将重点放在收集应用程序需求,而不是设计程序上。
XP避免了编写不需要的功能。
XP方法将精力集中在测试上。传统开发模型建议先编码,然后才生成测试接口;而XP首先生成单元测试用例,然后才编写代码通过测试。
XP的12个核心实践可归纳为:
 1、聆听客户和其他程序员的谈话。
 2、与客户合作,开发应用程序的规格说明和测试用例。
 3、结对编码。
 4、测试代码库。
XP有两个重要的原则:
 计划:XP的计划阶段与传统开发模型不同,通常将需求收集与应用设计结合起来。XP的计划重点是确定客户的应用需求,然后设计使用场景来满足客户的应用需求。通过生成使用场景,可以深入洞悉应用程序的目的和需求。此外,在验收测试中也可用到这些场景。
 极限编程的12个实践:
实践 注释
1、计划与需求分析 a将市场和业务开发人员集中起来,共同确认每个软件特征的最大商业价值。
b 以使用场景的形式重新编写每个重要的软件特征
c程序员估计完成每个使用场景的时间
d 客户根据估计时间和商业价值选择软件的功能特征
2、小规模、递增地发布 努力增加细微的、实在的、可增值的特征、频繁发布新版本
3、系统隐喻 编程小组确认隐喻,便于建立命名规则和程序流程
4、简要设计 实现最简单的设计,使代码通过单元测试。假设变更即将发生,因此不要在设计上花太多时间,只是不停的实现。
5、连续测试 在编写模块之前就生成单元测试用例。模块只有通过单元测试之后才告完成。程序只有在通过所有的单元测试和验收测试之后才算完成。
6、重构 清理和调整代码库,单元测试有助于确保在此过程中不破坏程序的功能。应在任何重构之后重新进行所有的单元测试。
7、结对编程 两个程序员协同工作,在同一台机器开发代码库。可使代码进行实时检查,能极大地提高缺陷的发现率和纠正率。
8、代码的集体所有权 所有代码归全体程序员所有,没有哪一个程序员只致力于开发某一个代码库。
9、持续集成 每天在变更通过单元测试之后将其集成到代码库中。
10、每周工作40小时 不允许加班。重大发布前一个星期例外。
11、客户在现场 开发人员和编程小组可以随时接触客户,这样可快速、准确地解决问题,使开发不致于中断。
12、按标准编码 所有的代码看上去必须一致。设计一个系统隐喻有助于满足该原则。
XP方法目前不太接受。
极限测试:概念
  极限测试方法强调连续测试。由单元测试和验收测试组成。目标都是确定程序中的错误。
  极限单元测试
   主要测试方法。有两个简单规则:
所有代码模块在编码开始之前必须设计好单元测试用例。
在产品发布之前必须通过单元测试。
 极限测试的单元测试与前面的单元测试的最大差别:极限测试中的单元测试必须在模块编码之前就设计和生成。
 在编码之前设计单元测试的好处:
  1、获得了代码将满足其规格说明的信心
  2、在开始编码之前,就展示了代码的最终结果
  3、更好地理解了应用程序的规格说明和需求
4、可以先实现一些简单的设计,稍后再放心地重构代码以改善程序的性能,而无需担心破坏应用程序的规格说明。
 以上优点对获得对应用程序规格说明和需求的洞察和理解不应被低估。
  验收测试
   目的是判断应用程序是否满足如功能性和易用性等其他需求。
极限测试的应用
  极限测试要求使用一个独立的黑盒测试方法,消除所有的偏见。
  测试用例设计(略)
  测试驱动器及其应用(略)
 小结
  极限编程:轻量级的开发过程,强调:沟通、计划和测试。
  极限测试重点:单元测试和验收测试。
  极限测试要求开始于程序编码之前,根据程序的规格说明设计测试配件。
第九章、测试因特网应用系统
  因特网本质为C/S 结构。客户端:Web浏览器;服务器端:Web或应用服务器。
电子商务的基本结构
  
  三层结构:
   第一层:Web服务器,又称“表示层”
   第二层:“业务层”,运行应用服务器。相关功能:
    1、事务处理
    2、用户身份鉴定
    3、数据确认
    4、程序日志
   第三层:“数据层”,从数据源(一个关系数据库管理系统RDBMS)中存储和获取数据。
测试的挑战
  测试基于因特网的应用系统所遇到的挑战:
   1、用户群庞大且五花八门:
   2、业务环境
3、测试环境
4、地点
5、安全性
6、浏览器的兼容性
7、网络连通性
测试的策略(略)
  表示层的测试
  业务层的测试
  数据层的测试


 


TAG: 性能测试 it 测试人员 测试知识 WEB应用程序 精华

 

评分:0

我来说两句

Open Toolbar