系统测试续一
8.8.4系统测试设计在系统测试需求确定后,可以开始测试设计工作。而测试设计又是整个测试过程中非常重要的一个环节,测试设计的输出结果是测试执行活动依赖的执行标准,测试设计的充分性决定了整个系统过程的测试质量。因此,为了保证系统测试质量,必须在测试设计阶段就对系统进行严密的测试设计。测试设计一般的流程是:首先理解软件和测试目标,然后设计测试用例,接着运行测试用例并处理测试结果,最后评估测试用例和测试策略。在确定测试设计流程时既强调目的性也强调计划性,目标是追求测试的高效率和测试的理想结果。当然,水能载舟,亦能覆舟。文档化和按部就班可以降低管理难度,增强计划性,但也可能扼杀测试人员的经验作用和灵感。1.理解软件和测试目标目的:建立软件故障模型,了解测试目标,来确定测试策略和测试计划。任务:了解软件的功能和业务背景、用户环境,了解软件的开发背景和系统结构、技术选型,了解软件的质量历史、版本变化,了解系统的测试目标和资源限制,确定测试策略和测试计划。方法:阅读软件使用手册,理解软件运行环境和用户行为,了解同类软件的功能和使用,了解软件要解决的问题域和解域(业务背景知识);试运行软件,从中熟悉软件功能,确定软件基本可以测试;了解软件体系结构、技术选型、开发环境和工具;阅读早期版本测试报告,以及单元测试和集成测试报告;确定测试人员限制和时间限制,制定初步测试策略和测试计划,确定测试结束标准。结果:1)建立错误模型,指导回答该软件可能的错误会出现在哪里(用户环境与测试环境不一致会不会出问题,没有测试过的代码或功能里面会不会出问题,没有测试过的输入组合、极端环境或功能使用方法、使用顺序等会不会出问题),如何做才能发现这些错误等问题.。2)在了解测试目标和资源限制之后,按照错误的性能价格比制定设计测试用例的优先级,并确定初步的测试策略和测试计划。3)确定测试结束标准或测试退出机制(已经解决的错误没有重现,所有缺陷报告已经关闭,所有测试用例全部执行完毕,通过错误播种、错误发生曲线分析、历史数据等相应方法统计出所遗留的未发现错误数量可以被接受,以及不属于技术层面且实际表明测试失败或者部分失败的市场和管理因素、预算和时间用完等因素)。2.设计测试用例目的:设计尽可能多、快、好、省发现错误的测试用例(即能够找到尽可能多的、以至于所有的BUG;能够尽可能快或早地发现最严重的BUG;找到的BUG是
关键的、用户最关心的,且找到BUG后能够重现找到的BUG,并为修正BUG提供尽可能多的信息;能够用最少的时间、人力和资源发现BUG,且测试的过程和数据可以重用)。任务:理解故障模型、理解现有的测试用例库、设计具体的测试用例。方法:采用基于故障模型如经验、历史数据/错误、软件开发和运行环境的软件攻击法(这需要创造性思维,而且要注意保证满足测试多、快、好、省的要求,并要有以孙子兵法进行指导的战役思想,当然还要记住没有银弹的教诲)。结果:测试用例(IE4.0的测试用例数目:10万)。测试用例没有标准文档格式,对于特殊人或在特殊情况下可以在运行后再形成文档。测试用例文档由简介和测试用例两部分组成:简介部分描述了测试目的、测试范围、定义术语、参考文档、概述等;测试用例部分逐一列出各测试用例(包含的要素为标题和编号、版本号、修改记录等,针对目标和假设前提/可能发现的错误,输入和数据/代码,测试步骤,预期输出和错误发现方法)。表8-3是一个简单的测试用例设计表格。表8-3测试用例设计表测试用例ID输入预期结果实际结果测试统计利率贷款期限(年)贷款金额(元)月支付总支付总利息月支付总支付总利息通过/失败测试日期测试人员TC-001 80 80000 578.01 TC-002 8.50 80000 615.13 TC-003 8.5 80000 787.79 3.运行测试用例并处理测试结果目的:使用测试用例发现错误并关闭错误。任务:运行测试用例并记录结果,评估测试结果并记录缺陷,处理缺陷直至缺陷关闭(即修改、延迟处理、不修改、不是错误)。方法:选择测试用例库中的测试用例运行,选择新设计的测试用例运行,录制/回放或者笔录中间步骤和结果,记录下执行过程中的灵感(但不要轻易修改本次执行任务),分析测试结果并尽量重现和优化错误步骤,详细填写缺陷报告并提供尽可能多的信息(如尽可能提供错误分析和修改建议),认真审核错误处理结果并及时关闭缺陷报告。结果:记录下的运行结果,记录下的新的测试用例设计思路,提交并处理的缺陷报告。4.评估测试用例和测试策略目的:检验测试用例和测试策略的有效性,必要时对测试用例和测试策略进行完善和修改,增加测试经验。任务:根据测试结果完善、修改、合并测试用例,如果没有文档化测试用例,此时需要文档化;另外,对测试用例库进行维护。即增加新的测试用例(尤其是已经发现了错误的测试用例),删除不必要的测试用例(要谨慎,除非是功能改变),修改刚刚使用的测试用例(根据测试结果),合并部分测试用例;最后,根据测试结果完善和修正测试策略和测试计划、产生新的测试用例设计思路。方法:基于经验(发现了什么问题,这种问题出现的原因是什么,为什么会发现这种问题,还可以更快地发现吗?测试用例可以合并
吗?可能联想到还会出现什么问题--新的测试用例),流程控制/尤其是测试用例库的维护可以借助于工具实现。结果:优化的测试用例库,优化的软件故障模型,优化的测试策略和测试计划,测试的经验和新的测试用例设计思路。事实上,测试设计过程是循环往复的、并且过程中的每一步骤都可以返回前面的任何一个步骤,即使单独一个测试用例也可能经过以上步骤多次。另外,测试设计最重要的工作就是设计测试用例。测试用例的衡量标准:多、快、好、省。测试用例库是一种经验积累,是测试活动最宝贵的财富。最后,在系统测试中设计测试用例的最常用到的思路是软件攻击。8.8.5系统测试中的软件攻击测试如同打仗,孙子兵法中的\知己知彼、百战不殆;攻其薄弱、避其锋芒;分而治之、逐个歼灭\作战策略在软件测试,特别是软件的系统测试中是非常具有指导意义的。作战的最主要手段就是进攻或攻击。软件测试中的攻击--软件攻击就是要寻找系统中最容易出现错误的地方进行测试(如寻找开发过程中容易出现疏忽的地方),保证多快好省地找出错误。软件攻击的核心是基于故障模型的测试用例设计。1.软件故障模型故障模型是将测试员的经验和直觉尽量归纳和固化,使得可以重复使用。测试员通过理解软件在做什么,来猜测可能出错的地方,并应用故障模型有目的地使它暴露错误。可以认为故障模型类似于系统设计中的定式(Pattern)思想,具体的攻击方法就是一个一个的定式。因此,对测试员来说,首先是要能够构造出一个准确的故障模型,然后使用该故障模型来决定测试策略、测试设计和测试用例运行。在建立故障模型时,希望故障模型在框架上是通用的,但是建立具体的故障模型时一定要针对具体的软件类型、应用环境、甚至开发工具才有意义。在进行故障模型设计时,要重点考虑软件的行为特性。如,软件功能和技术特点(包括输入、输出、数据以及处理等),软件操作环境(用户界面、文件系统、操作系统环境以及其他软件及操作系统API等)。按照这种思路设计的故障模型是一个二维模型,该模型从软件功能和技术特点来说,只接受正确的输入并正确地处理,只输出用户接受的并且正确的输出,保持数据结构的完整性(数值、精度和位置)和自我保护的合法的计算、功能交互和数据共享;而从软件操作环境来说:关注用户界面,关注文件系统接口、数据库系统接口,关注资源调配和管理,以及关注系统API调用。2.典型攻击方法基于上述软件行为特性的分析,和故障模型设计的考虑,重点关注一些典型的攻击方法。这些攻击方法从4个方面进行考虑:首先是何时施加攻击(Where),即软件在实现什么功能的时候适合使用这种攻击,这种攻击针对的功能是什么;其次是什么样的软件故障会使攻击成功(Why),即本攻击方法主要
会暴露实现过程中哪方面的问题,在实现技术上是什么原因产生了这种错误;然后是如何确定攻击暴露了失效(What),即本攻击方法成功的标志是什么?需要掌握业务背景知识,或者说理解什么是预期的输出结果;最后是如何进行攻击(How),即本攻击方法的操作步骤。下面就25种典型攻击方法(Pattern)为例来进行攻击方法的说明。1)用户接口输入攻击6种320使用非法输入:所有非法输入是否有错误处理代码,代码是否正确。321攻击使用默认值的输入:变量是否初始化了。322使用特殊字符集和数据类型的合法输入:是否正确处理了特殊字符和数据类型。323使用使缓冲区溢出的合法输入:是否检查字符串/缓冲区的边界了。324使用可能产生错误的合法输入组合:是否考虑了输入之间的组合关系。325重复输入相同的合法输入序列:是否考虑了循环处理的边界。2)用户接口输出攻击4种326产生同一个输入的各种可能输出:一个正确的输入在不同情况下产生不同的输出,这些不同情况是否都考虑充分。327强制产生不符合业务背景知识的无效的输出:开发人员是否了解解域/业务背景知识,是否会产生不符合业务背景的输出。328强制通过输出修改一些属性:初始化代码和修改代码是否同步。329检查屏幕刷新:屏幕刷新时机是否正确,屏幕刷新区域计算是否正确。3)用户接口数据攻击3种330制造使内部数据与输入的组合不相容的情况:处理输入的时候,是否考虑到内部数据的各种可能的组合。331制造使已有内部数据结构集合溢出的情况:上溢--增加一个元素到集合中,下溢--删除集合中最后一个元素,或者从空集合中删除元素。332制造使已有内部数据结构不符合约束的情况:初始化代码和修改代码是否同步。4)用户接口计算攻击4种333使用非法的操作数和操作符组合,攻击用户可以控制计算要求的情况:是否考虑到操作符和计算要求的合法性。334使函数递归调用自身:是否考虑到循环/递归的中止。335使计算结果溢出:考虑数据结构是否能够正确存储可能的计算结果。336攻击共享数据或互相依赖的功能计算:一个函数在修改共享数据的时候是否考虑到其他函数对这个共享数据的访问。5)文件系统介质攻击3种337使文件系统超载:检查文件访问函数的返回值,是否正确处理文件访问失败的情况。338使介质处于忙或者不可用状态:检查文件/介质访问函数的返回值,是否正确处理文件/介质访问失败的情况。339损坏介质(这时候OS可能认为介质可用):检查文件/介质访问函数的返回值,是否正确处理文件/介质访问失败的情况。6)文件系统文件攻击3种340使用特殊字符/特殊长度/无效的文件名:处理文件名的代码是否考虑到各种情况。341改变文件访问权限:访问文件的函数是否考虑到文件访问权限;文件访问失败,
是否有正确处理错误的代码。342使文件内容错误,并让系统使用这个文件:是否检查文件访问函数的返回值,错误处理代码是否正确。7)操作系统和软件接口攻击2种343记录-仿真攻击:模拟操作系统和操作环境故障,并记录软件对该故障的反应。软件是否正确处理内存故障/网络故障等操作系统和软件接口故障。344观察-失效攻击:观察底层API调用,并动态修改API调用,制造错误。软件是否正确处理操作系统和软件API调用错误的情况。本攻击方法一般用于对可靠性和稳定性要求非常高的软件。上述攻击中的许多要用到软件攻击工具,如:文件系统介质攻击,文件系统文件攻击,操作系统和软件接口攻击等。否则,攻击成本太高--例如物理毁坏介质;工作量太大--例如使用大文件填充硬盘、使用多任务抢占CPU/网络/内存等资源;有一些不容易实现--例如动态监控和修改API调用。一般采用软件故障植入的方法设计软件攻击工具。3.软件故障植入一般软件中的程序代码分为功能代码(通过实现用户需求来完成软件任务)和异常代码(通过异常或者其他错误处理机制来处理程序错误),故障植入的目标是强制执行异常代码(没有异常代码也是一种错误),从而发现其中的错误。故障植入方法分为编译期植入(在源代码中插入引发故障现象的代码)和运行期植入(在目标代码或运行环境中植入引发故障现象的代码)两类。而运行期软件故障植入具有更大的优势:(1)不需要源代码,因为系统测试阶段经常没有源代码。(2)可以达到模拟环境故障的目标,比如:网络中断、网络繁忙、内存匮乏等。(3)可以以API调用失败的方式模拟故障,因为在程序看来,任何环境故障实质上都是一系列的API调用失败。(4)可以只影响被植入的程序,因为使用的是模拟API调用失败方式。运行期软件故障植入可通过截获API的方式来实现。例如:基于调用源截获,找出程序中调用API的名字或DLL,然后用自己的函数替换掉;路径内截获,如果程序中使用VTable等技术间接记录调用API地址,则直接修改VTable中的API地址,用自己的函数替换掉;目的地截获,修改被调用API地址,修改头部代码,转向自己的函数,这实际上采用的是病毒方式。运行期软件故障植入的实现策略有两种:1)基于模式的故障植入:模拟环境故障,记录故障特征和影响到的API,修改影响到的API;采用乱棒打师傅方式的软件攻击法-canned HEAT(Hostile Enviroment Application Tester),并记录下由此引起的故障产生情况。2)基于调用的故障植入:观察使用的API,并独立地、细粒度地修改影响任何一个API;采用偷梁换柱、李代桃僵的方式进行软件攻击,并观察其破坏性而引起的失效攻击。8.8.6系统测试中的典型测试类型介绍1.错误处理测试健壮性是软件质量的一个重要因素。错