第09章软件测试.docx
《第09章软件测试.docx》由会员分享,可在线阅读,更多相关《第09章软件测试.docx(43页珍藏版)》请在冰豆网上搜索。
第09章软件测试
第九章软件测试
在软件开发过程中,特别是在开发大型软件系统的过程中,面对的问题是极其复杂。
因此,在软件生命周期的每个阶段就不可避免地会产生差错。
应该在每个阶段结束之前通过严格的技术审查,尽可能早地发现并纠正差错。
但是,经验表明审查并不能发现所有差错,此外在编码过程中还不可避免地会引入新的错误。
如果在软件正式运行之前,没有发现并纠正软件中的大部分差错,则这些差错迟早会在生产过程中暴露出来,那时不仅改正这些错误的代价更高,而且往往会造成很恶劣的后果。
测试的目的就是要在软件投入生产性运行之前,尽可能多地发现软件中的错误。
大量统计资料表明,软件测试的工作量往往占软件开发总工作量的40%以上,在极端情况,测试软件所花费的成本,可能相当于软件工程其他开发步骤总成本的三到五倍。
因此,必须高度重视软件测试工作。
软件测试是软件质量保证的关键步骤,代表了规约、设计和编码的最终检查。
对软件进行测试的结果也是分析软件可靠性的重要依据。
本章着重讨论软件测试概念、传统软件和面向对象软件测试的问题。
9.1软件测试概述
既然软件测试是必不可少的阶段,那么我们必须首先对软件测试要有一个比较全面的认识。
它的目标是什么?
遵循的原则是什么?
使用的方法又如何?
等等。
9.1.1软件测试目标
从表面看来,软件测试的目的与软件工程所有其他阶段的目的都相反。
软件工程的其他阶段都是“建设性”的,软件工程师力图从抽象的概念出发,逐步设计出具体的软件系统,直到用一种适当的程序设计语言写出可以执行的程序代码。
但是,在测试阶段测试人员努力设计出一系列测试方案,目的却是为了“破坏”已经建造好的软件系统——竭力证明程序中有错误不能按照预定要求正确工作。
当然,这种反常仅仅是表面的,或者说是心理上的。
暴露问题并不是软件测试的最终目的,发现问题是为了解决问题,测试阶段的根本目标是尽可能多地发现并排除软件中潜藏的错误,最终把一个高质量的软件系统交给用户使用。
以下三点可以看作是测试的目标或定义:
(1)测试是为了发现程序中的错误而执行程序的过程;
(2)好的测试方案是尽可能发现迄今为止尚未发现的错误的测试方案;
(3)成功的测试是发现了至今为止尚未发现的错误的测试。
由此可以看出,测试的正确定义是“为了发现程序中的错误而执行程序的过程”。
这和某些人通常想象的“测试是为了表明程序是正确的”,“成功的测试是没有发现错误的测试”等等是完全相反的。
正确认识测试的目标是十分重要的,测试目标决定了测试方案的设计。
如果为了表明程序是正确的,就会设计一些不易暴露错误的测试方案。
相反,如果测试是为了发现程序中的错误,就会力求设计出最能暴露错误的测试方案。
测试只能查找出程序中的错误,不能证明程序中没有错误。
如果成功构造了测试(根据上述目标),则能够在软件中揭示错误。
另外,测试能证实软件依据规约所具有的功能及其性能需求,构造测试时的数据收集提供了软件可靠性以及软件整体质量的一些信息。
9.1.2软件测试原则
在设计有效的测试用例之前,软件开发者必须理解软件测试的基本原则。
软件测试的原则是:
(1)将“尽早地和不断地进行软件测试”作为软件开发者的座右铭。
不应把软件测试仅仅看做是软件开发的一个独立阶段,而应当把它贯穿到软件开发的各个阶段中。
坚持软件开发的各个阶段的技术评审,这样才能在开发过程中尽早发现和预防错误,把出现的错误克服在早期,杜绝某些发生错误的隐患。
(2)测试用例应由测试输入数据和与之对应的预期输出结果这两部分组成。
测试以前应当根据测试的要求选择测试用例(Testcase),用来检验程序员编制的程序,因此不但需要测试的输入数据,而且需要针对这些输入数据预期输出结果。
(3)程序员应避免检查自己的程序。
创建系统的软件开发人员并不是构造软件测试的最佳人选。
这不能与程序的调试相混淆。
调试由程序员自己来做可能更有效。
程序员应尽可能避免测试自己编写的程序,程序开发小组也应尽可能避免测试本小组开发的程序。
为了达到最佳效果,最好建立独立的软件测试小组或测试机构。
(4)在设计测试用例时,应当包括合理的输入条件和不合理的输入条件。
合理的输入条件是指能验证程序正确的输入条件,不合理的输入条件是指异常的、临界的、可能引起问题的输入条件。
软件系统处理非法命令的能力必须在测试时受到检验。
用不合理的输入条件测试程序时,往往比用合理的输入条件进行测试能发现更多的错误。
(5)充分注意测试中的群集现象。
在被测程序段中,若发现错误数目多,则残存错误数目也比较多。
这种错误群集性现象,已被许多程序的测试实践所证实。
根据这个规律,应当对错误群集的程序段进行重点测试,以提高测试投资的效益。
(6)严格执行测试计划,排除测试的随意性。
测试之前应仔细考虑测试的项目,对每项测试做出周密的计划,包括被测程序的功能、输入和输出、测试内容、进度安排、资源要求、测试用例的选择、测试的控制方式和过程等,还要包括系统的组装方式、跟踪规程、调试规程、回归测试的规定,以及评价标准等。
对于测试计划,要明确规定,不要随意解释。
(7)应当对每个测试结果做全面检查。
有些错误的征兆在输出实测结果时已经明显地出现了,但是如果不仔细地、全面地检查测试结果,就会使这些错误被遗漏掉。
所以必须对预期的输出结果明确定义,对实测的结果仔细分析检查,暴露错误。
(8)妥善保存测试计划、测试用例、出错统计和最终分析报告,为维护提供方便。
9.1.3软件测试方法
测试任何产品都有两种方法,如果已经知道了产品应该具有的功能,可以通过测试来检验是否每个功能都能正常使用。
如果知道产品内部工作过程,可以通过测试来检验产品内部动作是否按照规格说明书的规定正常进行。
前一个方法称为黑盒测试,后一个方法称为白盒测试。
对于软件测试而言,黑盒测试法把程序看成一个黑盒子,完全不考虑程序的内部结构和处理过程。
也就是说,黑盒测试是在程序接口进行的测试,它只检查程序功能是否能按照规格说明书的规定正常使用,程序是否能适当地接收输入数据产生正确的输出信息,并且保持外部信息(如,数据库或文件)的完整性。
黑盒测试又称为功能测试。
与黑盒测试法相反,白盒测试法是完全了解程序的结构和处理过程。
这种方法按照程序内部的逻辑测试程序,检验程序中的每条通路是否都能按预定要求正确工作。
白盒测试又称为结构测试。
粗看起来,不论采用上述哪种测试方法,只要对每一种可能的情况都进行测试,就可以得到完全正确的程序。
包含所有可能情况的测试称为穷尽测试,对于实际程序而言,穷尽测试通常是不可能做到的。
所以软件测试不可能发现程序中的所有错误。
我们的目的是要通过测试保证软件的可靠性,因此,必须仔细设计测试方案,力争用尽可能少的测试发现尽可能多的错误。
9.1.4软件测试与软件开发各阶段的关系
软件开发过程是一个自顶向下、逐步细化的过程,而测试过程则是自底向上、逐步集成的过程。
低一级测试为上一级测试准备条件,如图9-1所示。
首先对每个程序模块进行单元测试,消除程序模块内部在逻辑上和功能上的错误和缺陷。
再对照软件设计进行集成测试,检测和排除子系统(或系统)结构上的错误。
随后再对照需求分析,进行确认测试。
最后,从全局出发运行系统,检验是否满足预期要求。
(测试的过程将在本章第二节中详细讨论。
)
图9-1软件测试与软件开发的关系
与开发过程类似,每个步骤在逻辑上是前一个步骤的继续。
大型软件系统通常由若干个子系统组成,每个子系统又由许多模块组成。
软件系统测试的基本步骤可分为模块测试又称为单元测试、子系统测试、系统测试(不论是子系统测试还是系统测试,都兼有检测和组装两重含义,通常称为集成测试)、确认测试。
关系重大的软件产品在验收之后并不立即投入生产性运行,而是再经过一段平行运行时间的考验。
测试作为软件工程的一个阶段,它的根本任务是保证软件的质量,因此除了测试之外,还要做一些与测试密切相关的工作。
这就是下面要讨论的内容。
9.1.5测试信息流
测试信息流如图9-2所示。
测试过程需要3类输入:
图9-2测试信息流
(1)软件配置包括软件需求规格说明、软件设计规格说明、源代码等。
(2)测试配置包括测试计划、测试用例、测试驱动程序等。
(3)测试工具测试工具为测试的实施提供某种服务。
例如,测试数据自动生成程序、静态分析程序、动态分析程序、测试结果分析程序以及驱动测试的工作等。
测试之后,用实测结果与预期结果进行比较。
如发现出错的数据,就要进行调试。
对已经发现的错误进行错误定位和确定出错性质,并纠正这些错误,同时修改相关的文档。
修正后的文档一般都要经过再次测试,直到通过测试为止。
通过收集和分析测试结果数据,对软件建立可靠性模型。
如果测试发现不了错误,那么可以肯定,测试配置考虑得不够细致充分,错误仍然潜伏在软件中。
这些错误最终不得不由用户在使用中发现,并在维护时由开发者去改正。
但那时改正错误的费用将比在开发阶段改正错误的费用要高若干倍。
9.1.6错误分类
由于人们对错误有不同的理解和认识,所以目前还没有一个统一的错误分类方法。
错误难于分类的原因,一方面是由于一个错误有许多征兆,因而它可以被归入不同的类。
另一方面是因为把一个给定的错误归于哪一类,还与错误的来源和程序员的心理状态有关。
(1)按错误的影响和后果分类
①较小错误:
只对系统输出有一些非实质性影响。
例如,输出的数据格式不符要求等。
②中等错误:
对系统的运行有局部影响。
如输出的某些数据有错误或出现冗余。
③较严重错误:
系统的行为因错误的干扰而出现明显不合情理的现象。
例如,开出了0.00元的支票,系统的输出完全不可信赖。
④严重错误:
系统运行不可跟踪,一时不能掌握其规律,时好时坏。
⑤非常严重的错误:
系统运行中突然停机,其原因不明,无法软启动。
⑥最严重的错误:
系统运行导致环境破坏,或是造成事故,引起生命、财产的损失。
(2)按错误的性质和范围分类
①功能错误
(a)规格说明错误:
规格说明可能不完全,有二义性或自身矛盾。
(b)功能错误:
程序实现的功能与用户要求的不一致。
这常常是由于规格说明中包含错误的功能、多余的功能或遗漏的功能所致。
(c)测试错误:
软件测试的设计与实施发生错误。
软件测试自身也可能发生错误。
(d)测试标准引起的错误:
对软件测试的标准要选择适当,若测试标准太复杂,则导致测试过程出错的可能就大。
②系统错误
(a)外部接口错误:
外部接口指如终端、打印机、通信线路等系统与外部环境通信的手段在使用中出错。
(b)内部接口错误:
内部接口指程序之间的联系。
它所发生的错误与程序内实现的细节有关。
例如,设计协议错、输入/输出格式错、数据保护不可靠、子程序访问错等。
(c)硬件结构错误:
这类错误在于不能正确地理解硬件如何工作。
例如,忽视或错误地理解分页机构、地址生成、通道容量、I/O指令、中断处理、设备初始化和启动等而导致的出错。
(d)操作系统错误:
这类错误主要是由于不了解操作系统的工作机制而导致出错。
当然,操作系统本身也有错误,但是一般用户很难发现这种错误。
(e)软件结构错误:
由于软件结构不合理或不清晰而引起的错误。
这种错误通常与系统的负载有关,而且往往在系统满载时才出现。
这是最难发现的一类错误。
(f)控制与顺序错误:
这类错误包括忽视了时间因素而破坏了事件的顺序,猜测事件出现在指定的序列中,等待一个不可能发生的条件,漏掉先决条件,规定错误的优先级或程序状态,漏掉处理步骤,存在不正确的处理步骤或多余的处理步骤,等等。
(g)资源管理错误:
这类错误是由于不正确地使用资源而产生的。
例如,使用未经获准的资源,使用后未释放资源,资源死锁,把资源链接在错误的队列中,等等。
③加工错误
(a)算术与操作错误:
指在算术运算、函数求值和一般操作过程中发生的错误。
(b)初始化错误:
典型的错误有忘记初始化工作区,忘记初始化寄存器和数据区,错误地对循环控制变量赋初值,用不正确的格式、数据或类型进行初始化,等等。
(c)控制和次序错误:
这类错误与系统级同名错误类似,但它是局部错误。
包括遗漏路径,不可达到的代码,不符合语法的循环嵌套,循环返回和终止的条件不正确,漏掉处理步骤或处理步骤有错,等等。
(d)静态逻辑错误:
这类错误主要包括不正确地使用CASE语句,在表达式中使用不正确的否定(例如用“>”代替“<”的否定),对情况不适当地分解与组合,混淆“或”与“异或”等。
④数据错误
(a)动态数据错误:
动态数据是在程序执行过程中暂时存在的数据。
各种不同类型的动态数据在程序执行期间将共享一个共同的存储区域,若程序启动时对这个区域未初始化,就会导致数据出错。
由于动态数据被破坏的位置可能与出错的位置在距离上相差很远,因此要发现这类错误比较困难。
(b)静态数据错误:
静态数据在内容和格式上都是固定的。
它们直接或间接地出现在程序或数据库中。
由编译程序或其他专门程序对它们做预处理。
这是在程序执行前防止静态错误的好办法,但预处理也会出错。
(c)数据内容错误:
数据内容是指存储于存储单元或数据结构中的位串、字符串或数字。
数据内容本身没有特定的含义,除非通过硬件或软件给予解释。
数据内容错误就是由于内容被破坏或被错误地解释而造成的错误。
(d)数据结构错误:
数据结构是指数据元素的大小和组织形式。
在同一存储区域中可以定义不同的数据结构。
数据结构错误主要包括结构说明错误及把一个数据结构误当做另一类数据结构使用的错误。
这是更危险的错误。
(e)数据属性错误:
数据属性是指数据内容的含义或语义。
例如,整数、字符串、子程序等。
数据属性错误主要包括对数据属性不正确地解释,例如错把整数当实数、允许不同类型数据混合运算而导致的错误等。
⑤代码错误
主要包括语法错误、打字错误、对语句或指令不正确理解所产生的错误。
(3)按软件生存期阶段分类
一般可把软件的逻辑错误按生存期不同阶段分为4类。
①问题定义(需求分析)错误
它们是在软件定义阶段,分析员研究用户的要求后所编写的文档中出现的错误。
换句话说,这类错误是由于问题定义不满足用户的要求而导致的错误。
②规格说明错误
这类错误是指规格说明与问题定义不一致所产生的错误。
可以将它们分成:
(a)不一致性错误:
规格说明中功能说明与问题定义发生矛盾。
(b)冗余性错误:
规格说明中某些功能说明与问题定义相比是多余的。
(c)不完整性错误:
规格说明中缺少某些必要的功能说明。
(d)不可行错误:
规格说明中有些功能要求是不可行的。
(e)不可测试错误:
有些功能的测试要求是不现实的。
③设计错误
这是在设计阶段产生的错误,它使系统的设计与需求规格说明中的功能说明不相符。
可以细分为:
(a)设计不完全错误:
某些功能没有被设计,或设计得不完全。
(b)算法错误:
算法选择不合适。
主要表现为算法的基本功能不满足功能要求、算法不可行或者算法的效率不符合要求。
(c)模块接口错误:
模块结构不合理,模块与外部数据库的界面不一致,模块之间的界面不一致。
(d)控制逻辑错误:
控制流程与规格说明不一致,控制结构不合理。
(e)数据结构错误:
数据设计不合理,与算法不匹配,数据结构不满足规格说明要求。
④编码错误
编码过程中的错误是多种多样的,大致有数据说明错、数据使用错、计算错、比较错、控制流错、界面错、输入/输出错及其他错误等几种。
在不同的开发阶段,错误的类型和表现形式是不同的,故应当采用不同的方法和策略来进行检测。
9.2软件测试过程与策略
在上一节中对软件测试与软件开发关系进行了分析,本节主要讨论软件测试过程中的单元测试、集成测试、确认测试。
单元测试集中对用源代码实现的每个程序单元进行测试,检查各个程序模块是否正确地实现了规定的功能。
然后,进行集成测试,根据软件设计规定的软件体系结构,把已测试过的模块组装起来,在组装时,检查程序结构组装的正确性。
确认测试则是要检查已实现的软件是否满足了需求规格说明中所确定的各种需求,以及软件配置是否完全、正确。
最后是系统测试,把已确认的软件纳入实际运行环境中,与其他系统成分组合在一起进行测试。
9.2.1单元测试
单元测试针对程序模块,进行正确性检验的测试。
其目的在于发现各模块内部可能存在的各种差错。
单元测试需要从程序的内部结构出发设计测试用例。
多个模块可以平行地独立进行单元测试。
(1)单元测试的内容
(a)模块接口测试:
对通过被测模块的数据流进行测试。
为此,对模块接口,包括参数表、调用子模块的参数、全程数据、文件输入/输出操作都必须检查。
(b)局部数据结构测试:
设计测试用例检查数据类型说明、初始化、缺省值等方面的问题,还要查清全程数据对模块的影响。
(c)路径测试:
选择适当的测试用例,对模块中重要的执行路径进行测试。
对基本执行路径和循环进行测试可以发现大量的路径错误。
(d)错误处理测试:
检查模块的错误处理功能是否包含有错误或缺陷。
例如,是否拒绝不合理的输入,出错的描述是否难以理解、是否对错误定位有误、是否出错原因报告有误、是否对错误条件的处理不正确,在对错误处理之前错误条件是否已经引起系统的干预,等等。
(e)边界测试:
要特别注意数据流、控制流中刚好等于、大于或小于确定的比较值时出错的可能性。
对这些地方要仔细地选择测试用例,认真加以测试。
此外,如果对模块运行时间有要求的话,还要专门进行关键路径测试,以确定最坏情况下和平均意义下影响模块运行时间的因素。
这类信息对进行性能评价是十分有用的。
(2)单元测试的步骤
通常单元测试在编码阶段进行。
在源程序代码编制完成,经过评审和验证,确认没有语法错误之后,就开始进行单元测试的测试用例设计。
利用设计文档,设计可以验证程序功能、找出程序错误的多个测试用例。
对于每一组输入,应有预期的正确结果。
模块并不是一个独立的程序,在考虑测试模块时,同时要考虑它和外界的联系,用一些辅助模块去模拟与被测模块相联系的其他模块。
这些辅助模块就是驱动模块和桩模块。
(a)驱动模块:
相当于被测模块的主程序。
用它接收测试用例的测试数据,把这些数据传送给被测模块,最后输出实测结果。
(b)桩模块(又称存根模块、连接模块):
用以代替被测模块所调用的子模块。
桩模块可以做少量的数据操作,不需要把子模块的所有功能都带进来,但不允许什么事情也不做。
被测模块、与被测模块相关的驱动模块和桩模块共同构成了一个“测试环境”,如图9-3所示。
图9-3单元测试的测试环境
如果一个模块要完成多种功能,且以程序包或对象类的形式出现,例如Ada中的包、MODULA中的模块、C++中的类。
这时可以将这个模块看成由几个小程序组成。
对其中的每个小程序先进行单元测试要做的工作,对关键模块还要做性能测试。
对支持某些标准规程的程序,要着手进行互联测试。
这种情况可称为模块测试,以区别单元测试。
为了进行单元测试必须编写测试软件,但是通常并不把它们作为软件产品的一部分交给用户。
许多模块不能用简单的测试软件充分测试,为了减少开销可以使用下面将要介绍的渐增式测试方法,在集成测试的过程中同时完成对模块的详尽测试。
模块的内聚程度高可以简化单元测试过程。
如果每个模块只完成一种功能,则需要的测试方案数目将明显减少,模块中的错误也更容易预测和发现。
9.2.2集成测试
集成测试是组装软件的系统技术,即在单元测试的基础上,需要将所有模块按照设计要求组装成为系统。
所以,这时需要考虑:
①在把各个模块连接起来的时候,模块接口的数据是否会丢失。
②一个模块的功能是否会对另一个模块的功能产生不利的影响。
③各个子功能组合起来,能否达到预期要求的父功能。
④全局数据结构是否有问题。
⑤单个模块的误差累积起来,是否会放大,从而达到不能接受的程度。
⑥单个模块的错误是否会导致数据库错误。
选择什么方式把模块组装起来形成一个可运行的系统,直接影响到模块测试用例的形式、所用测试工具的类型、模块编号的次序和测试的次序,以及生成测试用例的费用和调试的费用。
通常,把模块组装成为系统的方式有两种方式:
(1)一次性集成方式
它是一种非增殖式集成方式,也叫做整体拼装。
使用这种方式,首先对每个模块分别进行模块测试,然后再把所有模块组装在一起进行测试,最终得到要求的软件系统。
由于程序中不可避免地存在涉及模块间接口、全局数据结构等方面的问题,所以一次试运行成功的可能性并不很大。
(2)增殖式集成方式
又称渐增式集成方式。
首先对各个模块进行模块测试,然后将这些模块逐步组装成较大的系统,在组装的过程中边连接边测试,以发现连接过程中产生的问题。
最后,通过增殖逐步组装成为要求的软件系统。
这种方法实际上同时完成单元测试和集成测试。
(A)自顶向下的增殖方式:
将模块按系统程序结构,沿控制层次自顶向下进行集成。
这种增殖方式在测试过程中较早地验证了主要的控制和判断点。
在一个功能划分合理的程序结构中,判断常出现在较高的层次,较早就能遇到。
如果主要控制有问题,尽早发现它能够减少以后的返工。
把模块结合进软件结构的具体过程由下述四个步骤完成:
第一步,对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块;
第二步,根据选定的结合策略(深度优先或宽度优先),每次用一个实际模块代换一个存根程序(新结合进来的模块往往又需要新的存根程序);
图9-4自顶向下结合
第三步,在结合进一个模块的同时进行测试;
第四步,为了保证加入模块没有引进新的错误,可能需要进行回归测试(即,全部或部分地重复以前做过的测试)。
从第二步开始不断地重复进行上述过程,直到构造起完整的软件结构为止。
图9-4描绘了这个过程。
假设选取深度优先的结合策略,软件结构已经部分地构造起来了,下一步桩模块S8,将被模块M8取代。
M8可能本身又需要桩模块,以后这些桩模块也将被相应的模块所取代。
自顶向下的方法讲起来比较简单,但是实际使用时可能遇到逻辑上的问题。
这类问题中最常见的是,为了充分地测试软件系统的较高层次,需要在较低层次上的处理。
然而在自顶向下测试的初期,桩模块代替了低层次的模块,因此,在软件结构中没有重要的数据自下往上流。
为了解决这个问题,测试人员有两种选择:
第一把许多测试推迟到用真实模块代替了存根程序以后再进行;
第二从层次系统的底部向上组装软件。
第一种方法失去了在特定的测试和组装特定的模块之间的精确对应关系,这可能导致在确定错误的位置和原因时发生困难。
后一种方法称为自底向上的增殖方式,下面讨论这种方法。
(B)自底向上的增殖方式:
从程序结构的最底层模块开始组装和测试。
因为模块是自底向上进行组装,对于一个给定层次的模块,它的子模块(包括子模块的所有下属模块)已经组装并测试完成,所以不再需要桩模块。
在模块的测试过程中需要从子模块得到的信息可以直接运行子模块得到。
用下述步骤可以实现自底向上的结合策略:
第一把低层模块组合成实现某个特定的软件子功能的族;
第二编一个驱动模块(用于测试的控制模块),协调测试数据的输入和输出;
第三对由模块组成的子功能族进行测试;
第四去掉驱动模块,沿软件结构自下向上移动,把子功能族组合起来形成更大的子功能族。
图9-5自底向上结合
上述第二步到第四步实质上构成了一个循环。
图9-5描绘了自底向上的结合过程。
首先把模块组合成族1、族2和族3,使用驱动程序对每个子功能族进行测试。
族1和族2中的模块附属于模块M2,去掉驱动模块D1和D2,把这两个族直接同M2连接起来。
类似地,在和模块M3结合之前去掉族3的驱动程序D3。
最终M2和M3这两个模块都与模块M1结合起来。
随着结合向上移动,对测试驱动模块的需要也减少了。
事实上,如果软件结构的顶部两层用自顶向下的方法组装,可以明显减少驱动模块的数目,而且族的结合也将大大简化。
可以看出,自底向上测试方法的优缺点与上述自顶向下测试方法的优缺点刚好相反。
一般说来,纯粹自顶向下或纯粹自底向上的策略可能都不实用,人们在实践中创造出许多混合策略。
(C)混合增殖式测试
自顶向下增殖的方式和自底向上增殖的方式各有优缺点。
自顶向下增殖方式的缺点是