目录
一、软件测试基本知识
1.1 黑盒和白盒测试
1.1.1 黑盒测试
1.1.2 白盒测试
1.2 测试原则
二、代码检查、走查与评审
三、测试用例的设计
3.1 白盒测试
3.1.1 语句覆盖
3.1.2 判定覆盖
3.1.3 条件覆盖
3.1.4 多重条件覆盖
3.2 黑盒测试
3.2.1 等价划分
3.2.2 边界值分析
3.2.3 因果图
3.2.4 测试策略
四、模块测试
4.1 增量测试
4.2 自顶向下的测试
4.3 自底向上的测试
五、 更高级别的测试
5.1 功能测试
5.2 系统测试
六、 敏捷开发模式下的测试
又称数据驱动测试。将程序视为一个黑盒子,将重点集中在发现程序不按规范正确运行的环境条件。想用这种方法来发现程序的所有错误,判定的标准就是“穷举输入测试”,将所有可能的输入条件都作为测试用例。
又称逻辑驱动测试。对程序内部的逻辑进行检查,即将程序中的每条语句至少执行一次,称为穷举路径测试。
1)必须对预期输出或结果进行定义。
2)应当彻底检查每个测试的执行结果。
3)测试用例的编写不仅应当根据有效和预料到的输入情况,而且也应当根据无效和未预料到的输入情况。
代码检查与走查都要求人们组成一个小组来阅读或直观检查特定的程序。
错误类型 | 错误内容 |
---|---|
数据引用错误 | 是否有引用的变量未赋值或未初始化 |
对于所有数组的引用,是否每一个下标的值都在相应规定的范围内 | |
对于所有数组的引用,是否每一个下标都是整数 | |
对于所有通过指针或引用变量的引用,当前引用的内存单元是否分配 | |
变量值的类型或属性是否与编译器所预期的一致 | |
数据声明错误 | 是否所有变量都进行明确声明 |
若变量没有明确声明,默认属性是否正确 | |
变量初始化是否正确 | |
变量是否被赋予正确长度和数据类型 | |
相似名称的变量初始化不要出错 | |
运算错误 | 是否存在不一样数据类型的运算 |
是否有相同数据类型,不同字长变量间的运算 | |
表达式是否存在向上或向下溢出的情况 | |
除法运算中除数是否可能为0 | |
比较错误 | 是否有不同类型变量之间的比较 |
控制流程错误 | 警惕go to语句 |
是否所有循环都能够终止 | |
switch语句应该有default语句 | |
接口错误 | 形参数量是否等于实参数量 |
实参量纲与形参量纲是否匹配 | |
如果有全局变量,在所有引用它们的模块中,定义和属性是否相同 | |
输入/输出错误 | 打开文件的语句中,各项属性设置是否正确 |
是否有足够内存空间,来保存程序将读取的文件 | |
所有文件使用之前是否都已经打开 | |
所有文件使用之后是否都已经关闭 | |
是否有判断文件结束的条件,并正确处理 | |
对I/O出现的情况处理是否正确 | |
程序是否正确处理“文件无法找到”这类错误 | |
其他错误 | 对警告信息应注逐一排除 |
程序或模块是否具有鲁棒性,即对输入的合法性进行检查 | |
程序是否遗漏某个功能 |
本章讨论黑盒和白盒各自的测试方法。推荐先使用黑盒测试方法,然后视情况需要使用白盒测试。
完全的白盒测试是将程序中每条路径都执行一遍,然后对一个带循环的程序来说,完全的路径测试并不切合实际。
语句覆盖是指每条语句都执行一遍。例如下面的测试小程序:
public void foo(int a, int b, int x)
{
if(A > 1 && B == 0)
X = X/A;
if(A == 2 || X > 1)
X = X + 1;
}
按照语句覆盖准则,根据被测试的程序流程图3-1中,测试用例只需测试ace路径,而其他路径的代码都未被测试。
判定覆盖准则要求每个判断都至少有一个为真和为假的输出结果,并且每个判断都被执行至少一次。
与条件覆盖相比,判定覆盖只关心判断表达式的值是“真”是“假”,而条件判定则还关心判断表达式内部各个值的真假。举个例子:判定表达式:a>1 or b>1
用判定覆盖设计的测试数据:
a=5 (判定表达式的值为“真”),这里不需要管b的取值,就已经满足判定覆盖的条件了。
a=0 (判定表达式的值为“假”)
用条件覆盖设计的测试数据:
a=5 (条件a>1的值为“真”)
a=0(条件a>1的值为“假”)
b=5 (条件b>1的值为“真”)
b=0 (条件b>1的值为“假”)
这里不考虑 a>1 or b>1 这个表达式的取值的情况,而把a>1 和 b>1 这两个条件的取值考虑全。
所以综合来说,条件覆盖准则比判断覆盖准则要强一点。
由于有些条件会屏蔽其他条件,常常不能全部都执行完,可以选择多重条件覆盖准则来将判定覆盖和条件覆盖综合,即将每个判定中的所有可能条件结果的组合都执行一遍。
在寻找测试用例中,我们应该寻找一个最小的子集,该子集包含了绝大部分测试用例,但子集的数量却又比较少。这就需要我们进行划分等价类。假设测试每个等价类的代表性数据等同于测试该类的其他任何数据,这样就不用改等价类的其他测试用例。
使用该方法主要分为两步:1)确定等价类;2)生成测试用例。
1、确定等价类
确定等价类是选取每一个输入条件,并将其划分为两个或更多组。可以使用表3-1进行划分。
外部条件 | 有效等价类 | 无效等价类 |
---|---|---|
确定等价类是一个启发式过程,下面给出一些指导原则:
a、如果输入条件是一个范围(例:数量为1~999),那么可以确定一个有效等价类(1<数量<999),以及两个无效等价类(数量<=1,数量>=999);
b、如果输入条件规定了输入集合,并且每个值有不同处理(例:交通工具为汽车、卡车、出租车),那么应为每个值确定一个有效等价类(“汽车”)和无效等价类(“拖车”);
如果有程序并未等同处理等价类的元素,应该将这个等价类划分成更小地等价类。
2、生成测试用例
a、编写新测试用例时,尽可能多地覆盖尚未包含的有效等价类,直到所有有效等价类都被测试用例覆盖;
b、编写新测试用例时,覆盖一个且仅一个尚未覆盖的无效等价类,直到所有无效等价类都被测试用例覆盖。
用单个测试用例覆盖无效等价类,是因为某些特定的输入条件可能屏蔽其他条件。例:“请输入书籍类型(硬皮、软皮和活页)和书籍数量(1~999)”,代表两个错误输入:书籍类型错误和书籍数量错误。测试用例“XYZ 0”可能不会执行对数量的检查,程序可能提示XYZ是未知书籍类型就不检查其他错误了。
边界条件是指输入和输出等价类中那些恰好处于边界或超出边界的状态。一般来说,考虑边界条件的测试用例与没有考虑边界条件的测试用例相比,具有更高的测试回报率。
边界值分析不仅仅关注输入条件(输入等价类)的不同,还关注结果空间(输出等价类)。例:计算一个班学生的成绩。从输入条件看,测试用例需要有以下情况:0个学生成绩;成绩为负数;成绩为满分+1。从输出条件看,测试用例需要有以下情况:所有学生均为0分;所有学生均为满分。
等价划分和边界值分析没有对输入条件的组合进行分析,比如两个或多个条件组合或超过某个阈值。而因果图可以用一个系统的方法选择出高效地测试用例,并能指出规格说明的不完整性和不明确处。
在图3-2中,(1)表示当a=1时,b=1;
(2)表示当a=1时,b=0;
(3)表示当a、b、c中至少一个等于1时,d=1;
(4)表示当a、c全等于1时,d=1;
在图3-3中,(1)中E表示a、b中最多只有一个为1;
(2)中I表示当结果为1时,a、b、c至少有一个为1;
(3)中O表示有且仅有一个为1;
(4)中R表示当a为1时,b也要为1。
使用场景(User-Story):交通一卡通充值模拟系统
步骤一:认真分析规范说明一确定“因”和“果”。“因”如下:
1)投币50元
2)投币100元
3)充值50元
4)充值100元
“果”如下:
5)成功充值并退卡
6)找零
7)错误提示并退卡
“因”中,(1)和(2)互斥、(3)和(4)互斥;“果”中,(5)和(7)互斥。以上,可以画出因果图:
根据因果图可以绘制判定表,根据判断表可以设计测试场景。
场景1 | 场景2 | 场景3 | 场景4 | 场景5 | 场景6 | 场景7 | 场景8 | ||
---|---|---|---|---|---|---|---|---|---|
输入条件 | 1.投入50元 | 1 | 1 | 1 | |||||
2.投入100元 | 1 | 1 | 1 | ||||||
3.充值50元 | 1 | 1 | 1 | 1 | |||||
4.充值100元 | 1 | 1 | |||||||
输出条件 | 5.充值成功 | 1 | 1 | 1 | |||||
6.找零 | 1 | 1 | 1 | 1 | |||||
7.充值失败 | 1 | 1 | 1 | 1 | 1 |
每一种方法都可以提供一组具体的测试用例,但不能单独地提供完整的测试用例集,一组合理的策略如下:
1、如果规格说明中包含输入条件组合的情况,应首先使用因果图分析方法;
2、在任何情况下,都需要使用边界值分析;
3、应为输入和输出确定有效和无效等价类;
4、针对以上测试用例集,检查程序的逻辑结构,应使用判定覆盖、条件覆盖、多重条件覆盖准则,增加测试用例。
模块测试是对程序中单个子程序进行测试的过程,其目的在于将模块功能和定义模块的功能规格说明书或接口规格说明书进行比较。
在执行模块测试过程中,主要有两点考虑:如何设计一个有效的测试用例集;如何将模块组装成工作程序。第二点涉及以下内容:模块测试用例的编写形式;可能用到的测试工具类型;模块编码与测试顺序;生成测试用例成本和调试成本。
软件测试若先独立测试每个模块,然后将模块组装成完整的程序,这种测试方法称为非增量测试;若先将下一步测试的模块组装到测试完成的模块集合中,然后进行测试,这种测试方法称为增量测试。
有几个显而易见的结论:
1、非增量测试所需的工作量要多一些;
2、如果用增量测试,可以尽早发现模块中与不匹配接口、不正确假设相关的编程错误;
3、如果用增量测试,更有可能发现模块错误。比如测完模块A后,将模块A与模块B连接测试,可能还会发现模块A中的错误,可以更快速地定位到模块A的错误;
4、非增量测试所占用的机器时间少一些,但需要更多的驱动模块;
5、使用非增量测试,可以执行并行测试。
由于硬件成本的降低和人力成本的上升,增量测试的优点不断提升。综合考虑,增量测试优于非增量测试。
自顶向下的测试是从程序顶部或初始模块开始。以图4-1为例,A至L代表程序的12模块,其中模块J包含I/O读操作,而模块I包含I/O写操作。
第一步:测试模块A。要求编写出代表B、C、D的桩模块;
第二步:测试模块A-B或A-C或A-D。建议尽早添加关键模块或I/O模块进入测试。
第三步:按第二步建议,测试顺序可能为A-B-F-J-D-I。这样可尽早发现人为因素的问题,同时可以将程序演示给最终用户看。
第四步:继续增加新模块测试。
自顶向下的测试有两个显著缺点:
1)新测试模块与测试数据引入点有一定“距离”,导致无法决定往新模块中添加哪些测试用例可以测试尽可能多的情况。
2)测试过程与程序设计过程重叠。若在测试较低层次时,可能需要对较高层次进行合理更改。
自底向上与自顶向下相反,可以建立所有测试环境问题,但必须等程序设计完成后才能开始测试。
优点 | 缺点 | |
---|---|---|
自顶向下 | 1.如果主要缺陷在顶层将非常有利 2.引入I/O功能后,提交测试用例会更容易 3.早期的程序框架可以进行演示 |
1.必须开发桩模块,而桩模块比较复杂 2.引入I/O功能前,提交测试用例困难 3.创建测试环境困难 4.观察测试输出困难 |
自底向上 | 1.如果主要缺陷在底层将非常有利 2.测试环境比较容易建立 3.观察输出容易 |
1.必须开发驱动模块 2.直到最后一个模块添加进去,程序才形成一个整体 |
功能测试是一个试图发现程序与其外部规格说明之间存在不一致的过程。外部规格说明是一份从最终用户的角度对程序行为的精确描述。在进行功能测试时,需要对规格说明进行分析以提炼测试用例。第三章的黑白盒测试是测试方法,第四章是测试用例的设计。
系统测试是一个试图发现软件与其初始目标不一致的过程。
分类 | 说明 |
---|---|
能力测试 | 确保程序的目标功能实现 |
容量测试 | 发现处理大容量数据时的程序异常 |
强度测试 | 发现在高强度(很短时间间隔)持续的数据处理中的异常 |
可用性测试 | 评估最终用户在使用软件并与软件交互时的可用性问题 |
安全性测试 | 试图攻破程序的安全防线 |
性能测试 | 评估程序的响应时间以及吞吐量瓶颈 |
存储测试 | 确保程序可以正确处理存储需求,包括系统存储和物理上的存储 |
配置测试 | 检查程序在推荐配置是否能运行 |
兼容性测试 | 评估新版本能否兼容老版本 |
可靠性测试 | 评估程序的运行时长和MTBF(评估故障间隔时间)要求 |
可恢复测试 | 测试系统恢复相关功能是否按照要求实现 |
可维护测试 | 评估系统是否拥有日志机制、诊断程序 |
文档测试 | 检验用户文档是否准确 |
敏捷开发提倡迭代式和增量式的开发模式,并强调测试在其中的作用。这是围绕以用户为中心、以客户需求为导向的开发过程。
方法 | 描述 |
---|---|
敏捷建模 | 一种建模原则,用以支撑其他敏捷方法 |
敏捷统一过程 | 为敏捷量身定做的统一软件过程的精简版 |
动态系统开发方法 | 基于快速软件开发方法,依赖于客户的持续参与,使用迭代式和增量式的开发模式,目标是软件能及时交互 |
核心统一过程 | 只选择适合当前项目的实践 |
极限编程 | 另一种迭代式和增量式的开发模式,强调单元测试和验收测试 |
功能驱动开发(FDD) | 以客户提供的功能需求为驱动,频繁发布小版本,使用领域对象建模以及组建功能团队 |
开发统一过程 | 实现标准的统一过程 |
Scrum | 一种迭代式和增量式的管理方法,支持多个敏捷开发模式 |
进度跟踪 | 适用于所有敏捷方法,用来度量敏捷开发的速度和进度 |
极限编程(XP)至今是最流行的敏捷软件开发过程。XP适用于中小规模的软件开发,因为这些软件规格说明变更频繁,而且可以进行实时沟通。
XP的关注点:
1)实现简单的设计;
2)开发人员和客户的沟通协作;
3)不断地测试代码库;
4)重构适应规格说明的变更;
5)寻求用户的反馈。
XP开发模型用12个核心实践来驱动该过程。
实践 | 注释 |
---|---|
1.计划与需求分析 | 1.以使用场景形式编写每个软件特征 2.程序员估计完成每个场景时间 3.客户根据估计时间和商业价值选择软件的功能特征 |
2.小规模、递增发布 | 努力添加细微特征,频繁发布新版本 |
3.系统隐喻 | 建立命名规则和程序流程 |
4.简要设计 | 实现最简单设计 |
5.连续测试 | |
6.重构 | 清理和调整代码库,应在重构后重新进行单元测试 |
7.结对编程 | 两位程序员协同工作,极大地提高错误发现率和纠错率 |
8.代码集体所有权 | 代码归集体所有,没有那个程序员只致力于开发某一个代码库 |
9.持续集成 | 每天在变更通过单元测试后将其集成到代码库中 |
10.每周40小时工作 | 每周全力工作40小时后,就不需要加班。在重大发布前一星期例外 |
11.客户在现场 | 开发人员和编程小组课随时接触客户 |
12.按标准编码 |