测试是什么?
测试就是是为发现错误而执行程序的过程。 软件测试更适合被视为试图发现程序中错误(假设其存在)的破坏性的过程。
黑盒和白盒测试
黑盒测试和白盒测试都是是一种重要的测试策略。
其中黑盒测试 又称为数据驱动的测试或输入/输出驱动的测试。 测试目标与程序的内部机制和结构完全无关,而是将重点集中放在发现程序不按其规范正确运行的环境条件,简单说黑盒测试不需要我们去了解程序的内部结构,它是怎么实现。
而对于白盒测试,又称逻辑驱动的测试,与黑盒测试不同,它允许我们检查程序的内部结构,它是 对程序的逻辑结构迸行检查。
软件测试的十大原则
编号 |
原则 |
1 |
测试用例中一个必需部分是对预期输出或结果进行定义。 就是通过事先精确定义程序的预期输出,鼓励人们对所有的输出进行仔细检查。因此,一个测试用例必须包括 两个部分: 1.对程序的输入数据的描述。 2.对程序在上述输入数据下的正确输出结果的精确描述 |
2 |
程序员应避免测试自己编写的程序。 原因很简单,自己是很难发现自己的错误的,就比如自己做错了一道题,你以为是对的,但别人改的时候却给你答了个×。 让其他人来测试程序会更加有效,也会更容易测试成功。 |
3 |
编写软件的组织不应当测试自已编写的软件。 |
4 |
应当彻底检查每个测试的执行结果。 注意: 在后续测试中发现的错误,往往是前面的测试遗漏掉的,这是一个很容易忽略的原则 |
5 |
测试用例的编写不仅应当根据有效和预料到的输入情况,而且也应当根据无效 和未预料到的输入情况 |
6 |
检查程序是否“未做其应该做的”仅是测试的一半,测试的另一半是检查程是 否“做了其不应该做的” |
7 |
应避免测试用例用后即弃,除非软件本身就是个一次性的软件 |
8 |
计划测试工作时不应默许假定不会发现错误 |
9 |
程序某部分存在更多错误的可能性,与该部分已发现错误的数量成正比。 这提醒我们, 为了使测试获得更大的成效,最好对这些容易存在错误的部分进行额外的测试 |
10 |
软件测试是一项极富创造性,极具智力的挑战性的工作 |
总的来说,请牢记以下三个重要的测试原则:
• 软件测试是为发现错误而执行程序的过程。
• 一个好的测试用例具有较高的发现某个尚未发现的错误的可能性。
• 一个成功的测试用例能够发现某个尚未发现的错误。
人工测试的方法
代码检查、代码走查、桌面检查、同行评分。
一、代码检查过程的一个重要部分就是对照一份错误列表,然后来检查程序是否存在常见错误。
错误列表,常见的错误有:
数据引用错误,数据声明错误,运算错误,控制流程错误,比较错误,接口错误,输入输出错误。
数据引用错误 |
运算错误 |
1.是否有引用的变量未赋值或未初始化? |
1.是否存在非算术变量间的运算? |
2.下标的值是否在范围之内? |
2.是否存在混合摸式的运算? |
3.是否存在非整数下标? |
3.是否存在不同字长变量问的运算? |
4.是否存在虚调用? |
4.目标变量的大小是否小于赋值大小? |
5.当使用别名时属性是否正确? |
5.中间结果是否上溢或下溢? |
6.记录和结构的属性是否匹配? |
6.是否存住被 0 除? |
7.是否计算位串的地址?是否传递位串参 数? |
7.是否存在二进制的不精确度? |
8.基础的存储属性是否正确? |
8.变量的值是否超过了有意义的范围? |
9.跨过程的结构定义是否匹配? |
9.操作符的优先顺序是否被正确理解?。 |
10.索引或下标操作是否有“仅差一个”的错 误? |
10.整数除法是否正确? |
11.继承需求是否得到满足? |
数据声明错误 |
比较错误 |
1.是否所有的变量都已声明? |
1.是否存在不同类型变量间的比较? |
2.默认的属性是否被正确理解? |
2.是否存在混合模式的比较运算? |
3.数组和字符串的初始化是否正确? |
3.比较运算符是否正确? |
4.变量是否赋予了正确的长度,类型和存储 类? |
4.布尔表达式是否正确? |
5.初始化是否与存储类相一致? |
5.比较运算是是否与布尔表达式相混合? |
6.是否有相似的变量名? |
6.是否存在二进制小数的比较? |
7.操作符的优先顺序是否被正确理解? |
|
8.编译器对布尔表达武的计算方式是否被正 确理解? |
控制流程错误 |
输入/输出错误 |
1.是否超出了多条分支路径? |
1.文件的属性是否正确? |
2.是否每个循环都终止了? |
2.0PEN 语句是否正确? |
3.是否每个程序都终止了? |
3.I/O 语句是否符合格式规范? |
4.是否存在由于入口条件不满足而跳过循环 体? |
4.缓冲大小与记录大小是否匹配? |
5.可能的循环越界是否正确? |
5.文件在使用前是否打开? |
6.是否存在“仅差一个”的迭代错误? |
6.文件在使用后是否关闭? |
7.DO/END 语句是否匹配? |
7.文件结束条件是否被正确处理? |
8.是否存在不能穷尽的判断? |
8.是否处理了 I/O 错误? |
9.输出信息中是否有文字或语法错误? |
接口错误 |
其他检查 |
1.形参的数量是否等于实参的数量? |
1.在交叉引用列表中是否存在未引用过的变 量? |
2.形参的量纲是否与实参的量纲相匹配? |
2.属性列表是否与预期的相一致? |
3.形参的量纲是否与实参的量纲相匹配? |
3.是否存在“警告”或“提示”信息? |
4.传递给被调用模块的实参个数是否等于其 形参个数? |
4.是否对输入的合法性进行了检查? |
5.传递给被调用模块的实参属性是否与其形 参属性匹配? |
5.是否遗漏了某个功能? |
6.传递给被调用模块的实参量纲是否与其形 参量纲匹配? |
|
7.调用内部函数的实参的数量、属性,顺序是 否正确? |
|
8.是否引用了与当前入口点无关的形参? |
|
9.是否改变了某个原本仅为输入值的形参? |
|
10.全局变量的定义在模块间是否一致? |
|
11.常数是否以实参形式传递过? |
二、代码走查
以小组为单位进行代码阅读,是一系列规程和错误检查技术的集合。 代码走查也是采用持续一至两个小时的小间断会议的形式。
代码检查与代码走查的区别:
代码走查与代码检查很相似,都是以小组为单位进行代码阅读, 但是规程稍微有所不同,采用的错误检查技术也不一样。
三、桌面检查:
这是一种古老的方法,简单就是 由一个人阅读程序,对照错误列表检查程序,对程序推演测试数据。 对于大多数人而言,桌面检查的效率是相当低的。 桌面检查最好由其他人而非该程序的编写人员来完成。
四、同行评分:
人工评审方法与程序测试并无关系(其目标不是为了发现错误),却仍在这里谈到,这是因为它与代码阅读的思想有关。 同行评分是一种依据程序整体质量,可维护性、可扩展性、易用性和清晰性对匿名程序进行评价的技术。该项技术的目的是为程序员提供自我评价的手段。
测试用例的设计:
软件测试中最重要的因素就是设计和生成有效的测试用例。由于时间和成本的约束,最关键的问题就是:
在所有可能的测试用例中,哪个子集最有可能发现最多的错误?
黑盒测试和白盒测试的方法。(黑盒测试和白盒测试可以写一个博客)
推荐的步骤是先使用黑盒测试方法来设计测试用例,然后视情况需要使用白盒测试方法来设计补充的测试用例。
黑盒测试
一、等价类划分
一个好的测试用例描述为具有相当高的可能性发现某个错误来,此外还讨论了对程序的穷举输入测试是无法实现的。 我们要找的
子集必须是正确的,并且是可能发现最多错误的子集。
确定这个子集的一种方法,就是要意识到一个精心挑选的测试用例还应具备另
两个特性:
1. 严格控制测试用例的增加,减少为达到“合理测试”的某些既定日标而必须设计的其他测试用例的数量。
2. 它覆盖了大部分其他可能的测试用例。也就是说,它会告诉我们,使用或不使用这个特定的输入集合,哪些错误会被发现,哪些会被遗漏掉。
二、边界值分析:
所谓边界条件,是指输入和输出等价类中那些恰好处于边界、或超过边界、或在边界以下的状态。边界值分析方法与等价划分方法存在两方面的不同
1. 与从等价类中挑选出任意一个元素作为代表不同,边界值分析需要选择一个或多个元素,以便等价类的每个边界都经过一次测试。
2. 与仅仅关注输入条件(输入空间)不同,还需要考虑从结果空间(输出等价类)设计测试用例。
通用指南:
1. 如果输入条件规定了一个输入值范围,那么应针对范围的边界设计测试用例,针对刚刚越界的情况设计无效输入测试用例。举例来说,如果输入值的有效范围是-1.0 至+l.0,那么应针对-1.0、1.0、-1.001 和 1.001 的情况设计测试用例。
2. 如果输入条件规定了输入值的数量,那么应针对最小数量输入值、最大数量输入值,以及比最小数量少一个、比最大数量多一个的情况设计测试用例。举例来说,如果某个输入文件可容纳 l~255 条记录,那么应根据 0、l、255 和 256条记录的情况设计测试用例。
3. 对每个输出条件应用指南 1。
4. 对每个输出条件应用指南 2。
5. 如果程序的输入或输出是一个有序序列(例如顺序的文件、线性列表或表格),则应特别注意该序列的第一个和最后一个元素。
6. 此外,发挥聪明才智找出其他的边界条件。
三、因果图分析
边界值分析和等价划分的一个弱点是未对输入条件的组合进行分析。 因果图有助于用一个系统的方法选择出高效的测试用例集。它还有一个额外的
好处,就是可以指出规格说明的不完整性和不明确之处
因果图是一种形式语言,用自然语言描述的规格说明可以转换为因果图。因果图实际上是一种数字逻辑电路(一个组合的逻辑网络)、但没有使用标准的电子学符号,而是使用了稍微简单点的符号。除了了解布尔逻辑(了解逻辑运算符“与”、“或”、“非”)之外,读者不必掌握电子学方面的知识。
四、错误测试
利用直觉和经验猜测出错的可能类型,然后编写测试用例来暴露这些错误。 其基本思想是列举出可能犯的错误或错误易发情况的清单,然后依据清
单来编写测试用例。
测试策略
1. 如果规格说明中包含输入条件组合的情况,应首先使用因果图分析方法。
2. 在任何情况下都应使用边界值分析方法。应记住,这是对输入和输出边界进行的分析。边界值分析可以产生一系列补充的测试条件,但是,也正如
“因果图分析”一节所述,多数甚至全部条件都可以被整合到因果图分析中。
3. 应为输入和输出确定有效和无效等价类,在必要情况下对上面确认的测试用例进行补充。
4. 使用错误猜测技术增加更多的测试用例。
5. 针对上述测试用例集检查程序的逻辑结构。应使用判定覆盖、条件覆盖、判定/条件覆盖或多重条件覆盖准则(最后的一个最为完整)。如果覆盖准
则未能被前四个步骤中确定的测试用例所满足,并且满足准则也并非不可能(由于程序的性质限制,某些条件的组合也许是不可能实现的),那么增
加足够数量的测试用例,以使覆盖准则得到满足。
白盒测试
关注的是测试用例执行的程度或覆盖程序逻辑结构(源代码)的程度。
语句覆盖<判定覆盖<条件覆盖
语句覆盖
度量被测代码中每个可执行语句是否被执行到了。
判定覆盖
判定覆盖是一种比语句覆盖更强的准则,但仍然相当不足。
条件覆盖
比判定覆盖更强一些的准则, 在条件覆盖情况下,要编写足够的测试用例以确保将一个判断中的每个条件的所有可能的结果至少执行一次。
判定/条件覆盖
较强一些的逻辑覆盖准则, 该准则要求必须编写足够的测试用例,使得每一个判断都至少有一个为“真”和为“假”的输出结果。换句话
说,也就是每条分支路径都必须至少遍历一次。 缺点是尽管看上去所有条件的所有结果似乎都执行到了,但由于有些特定的条件会屏蔽掉其他的条件,常常并不能全部都执行到。
多重条件覆盖
总的来说,对于包含每个判断只存在一种条件的程序,最简单的测试准则就是设计出足够数量的测试用例,实现:
1)将每个判断的所有结果都至少执行一次;
2)将所有的程序入口(例如入口点或 ON 单元)都至少调用一次,以确保全部的语句都至少执行一次。而对于包含多重条件判断的程序,最简单的测试准则是设计出足够数量的测试用例,将每个判断的所有可能的条件结果的组合,以及所有的入口点都至少执行一次(加入“可能”二字,是因为有些组合情况难以生成)。
模块单元测试:
模块测试(或单元测试)是对程序中的单个子程序、子程序或过程进行测试的过程,也就是说,一开始并不是对整个程序进行测试,而是首先将注意力集中在对构成程序的较小模块的测试上面。 模块测试的目的是将模块的功能与定义模块的功能规格说明或接口规格说明进行比较。
模块测试的测试用例的设计过程如下:使用一种或多种白盒测试方法分析模块的逻辑结构,然后使用黑盒测试方法对照模块的规格说明以补充测试用例。
增量测试和非增量测测试的区别:
1.非增量测试所需的工作量要多一些。
2. 如果使用了增量测试,可以较早地发现模块中与不匹配接口、不正确假设相关的编程错误。
3. 因此如果使用了增量测试,调试会进行得容易一些,我们假定存在着与模块间接口或假设相关的编程错误(根据经验而来的合理假设),那么,如果
使用非增量测试,直到整个程序组装之后,这些错误才会浮现出来。
4. 增量测试会将测试进行得更彻底。
5. 非增量测试所占用的机器时间显得少一些。
6. 模块测试阶段开始时,如果使用的是非增量测试,就会有更多的机会进行并行操作(也就是说, 所有的模块可以同时测试)。
总的来说,第 1 条~第 4 条结论是增量测试的优点,而第 5、 6 条结论是其不利之处。 可以得出结论,增量测试要更好一些。
自顶向下测试与自底向上测试( 两种策略都属于增量测试):
1、自底向上的测试(或自底向上的开发) 常被错误地当作非增量测试。原因在于自底向上的测试的开展方式与非增量测试是相同的(即对底层或终端模块进行测试),但是就如我们从上一节看到的那样,自底向上的测试是一种增量测试。
自顶向下的测试:
自顶向下的测试是从程序的顶部或初始模块开始。
测试开始之后,挑选哪一个后续模块进行增量测试没有惟一正确的方法:惟一的原则是:要成为合乎条件的下一个模块,至少一个该模块的从属模块(调用它的模块)事先经过了测试。
自底向上的测试:
在大多数情况下,自底向上的策略与自顶向下的策略是相对立的。自顶向下测试的优点成为自底向上测试的缺点,而自顶向下测试的缺点又成为自底向上测试的优点。
自底向上的策略开始于程序中的终端模块(此类模块不再调用其他任何模块)。 同样没有最佳的方法来挑选要进行增量测试的下一个模块。
惟一正确的原则是,要成为合乎条件的下一个模块,该模块所有的从属模块(它调用的模块)都已经事先经过了测试。
比较:
自顶向下测试
优点:
1. 如果主要的缺陷发生程序的顶层将非常有利2. 一旦引人 I/O 功能,提交测试用例会更容易3.早期的程序框架可以进行演示,并可激发
积极性
缺点:
1. 必须开发桩模块2. 桩模块要比最初表现的更复杂3. 在引入 I/O 功能之前,向桩模块中引入测试用例比较用难4. 创建测试环境可能很难,甚至无法实现5. 观察测试输出很困难6. 使人误解设计和测试可以交迭进行7. 会导致特定模块测试的完成延后
自底向上测试
优点:
1. 如果主要的缺陷发生在程序的底层将非常有利2. 测试环境比较容易建立3. 观察测试输出比较容易
缺点:
1. 必须开发驱动模块2. 直到最后一个模块添加进去,程序才形成一个整体。
更高级别的测试
1.能测试是一个试图发现程序与其外部规格说明之间存在不一致的过程。
2. 系统测试最容易被错误理解,也是最困难的测试过程。 系统测试有着特定的目的:将系统或程序与其初始目标进行比较。
------------能力测试的过程是逐条语句地检查目标文档,当某条语句定义了一个“要做什么”, 就判断程序是否满足。
------------容量测试的目的是为了证明程序不能处理目标文档中规定的数据容量。
------------强度测试使程序承受高负载或强度的检验。 高强度是指在很短的时间间隔内达到的数据或操作的数量峰值。
------------易用性测试 试图发现人为因素或易用性的问题。
------------安全性测试
------------ 性能测试
------------ 存储测试
------------ 配置测试
------------ 兼容性/配置/转换测试
------------ 安装测试
------------ 可靠性测试
------------ 可恢复性测试 证明这些恢复机制不能够正确发挥作用。
------------ 适用性测试 软件还可能有适用性或可维护性的目标。
------------ 文档测试 检查用户文档的正确性
------------ 过程测试
------------ 系统测试的执行
3. 验收测试
4. 安装测试
调试
调试是执行一次成功的测试之后所要进行的工作。 调试是一个包含两个步骤的过程,从执行了一个成功的测试用例、发现了一个问题之后开始。第一步,确定程序中可疑错误的准确性质和位置;第二步,修改错误。
1.暴力法调试
2. 归纳法调试
3. 演绎法调试 演绎的过程是从一些普遍的理论或前提出发,使用排除和精炼的过程,达到一个结论
4. 回溯法调试 使用这个过程,可以确定程序中从状态符合预期值的位置点,到第一个状态不符合预期值的位置点之间的范围
5. 测试法调试 当发现了某个被怀疑的错误的症状之后,我们需要编写与原先有所变化的测试用例,尽量确定错误的位置。
调试的原则:
定位错误的原则
1. 动脑筋
2. 如果遇到了僵局,就留到稍后解决
3. 如果遇到了困境,就把问题描述给其他人听
4. 仅将测试工具作为第二种手段
5. 避免使用试验法——仅将其作为最后的手段
修改错误的技术:
1. 存在一个缺陷的地方,很有可能还存在其他缺陷
2. 应纠正错误本身,而不仅是其症状
3. 正确纠正错误的可能性并非 100%
4. 正确修改错误的可能性随着程序规模的增加而降低
5. 应意识改正错误会引入新错误的可能性
6. 修改错误的过程也是临时回到设计阶段的过程
7. 应修改源代码,而不是目标代码
参考文献:
《软件测试的艺术》------Glenford J.Myers