1、软件测试系列培训,(一)单元测试,刘东升 2007-04,浙 江 XXXX集 成 控 制 股 份 有 限 公 司,现象,投入太多的精力,找 bug,而新的代码仍然会出现类似 bug; 写完代码,心里没底,是否有大量 bug 等待自己; 新修改的代码不知道是否影响其他部分代码; 由于牵扯太多,导致不敢进行修改代码; .,主要内容,软件测试基本理论 单元测试基本理论 为什么要进行单元测试 C/C+单元测试问答 单元测试工具 如何实施单元测试 讨论,一、软件测试基本理论,目的:对软件测试有个整体认识 软件测试 软件测试分类 软件开发全过程检测及测试自动化 V模型与X模型 TDD( Test-Driv
2、en Development),什么是软件测试?,在软件投入运行前,对软件需求分析、设计规格说明和编码的最终复审,是软件质量保证的关键步骤。软件测试的概念:软件测试是为了发现错误而执行程序的过程。或者说,软件测试是根据软件开发各个阶段的规格说明和程序的内部结构而精心设计一批测试用例(即输入数据及其预期结果),并利用这些测试用例去执行程序,以发现程序错误的过程。,测试的目的,测试是程序的执行过程,目的在于发现错误; 一个好的测试用例在于能发现至今未发现的错误; 一个成功的测试是发现了至今未发现的错误的测试 也可以这样说,测试的目标是以较少的用例、时间和人力找出软件中潜在的各种错误和缺陷,以确保系
3、统的质量 一个被人忽略的软件测试目的是:测试可以帮助发现当前开发工作所采用的软件过程(也是一个“软件”)的缺陷,以便进行改进。 首先,测试并不仅仅是为了要找出错误。分析错误产生的原因和错误在开发的哪一个阶段产生,具有非常重要的意义。 通过分析错误产生于哪一个开发阶段、而又在哪一个阶段被发现,我们可以判断从错误的产生到错误的发现,跨越了多少个开发阶段。 软件开发的一条重要原则是尽早发现与修正错误。 正确分析与利用测试的结果,我们可以非常有效地进行软件过程改进,软件测试原则 2-1,完全测试程序是不可能的输入量太大输出结果太多软件实现途径太多软件说明书没有客观标准。从不同角度看,软件缺陷的标准不同
4、。,软件测试原则 2-2,软件测试是有风险的行为测试无法显示潜伏的软件缺陷 找到的软件缺陷越多,就说明软件缺陷越多 并非所有软件缺陷都能修复 软件测试一项讲究条理的技术专业,软件测试分类,从是否需要执行被测软件的角度,可分为: 静态测试 动态测试从测试是否针对系统的内部结构和具体实现算法的角度来看,可分为 : 白盒测试 黑盒测试,软件测试方法静态和动态,静态检查 确保系统按照组织的标准和过程运行,主要依赖于评审和非运行的手段来检查。通常包括需求评审、设计评审、代码走查和代码检查。 动态检查 在生命周期中进行测试(运行)。通常包括单元测试、集成测试、系统测试、用户的验收测试。,静态测试,审查 (
5、Inspection)软件的一种基本测试方法,它以一系列典型问题为依据进行检测。 走查 (Walkthrough) 一对一的审查,比审查更加仔细。 回顾(Review)以发现软件中存在的错误和缺陷为目的的一种软件测试方法,它是在软件证实执行之前完成。,静态和动态测试进行结构和功能测试,测试技术,黑盒测试,黑盒测试也称功能测试或数据驱动测试,它是在已知产品所应具有的功能,通过测试来检测每个功能是否都能正常使用,在测试时,把程序看作一个不能打开的黑盆子,在完全不考虑程序内部结构和内部特性的情况下,测试者在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输
6、入数锯而产生正确的输出信息,并且保持外部信息(如数据库或文件)的完整性。,“黑盒”测试着眼于程序外部结构、不考虑内部逻辑结构、针对软件界面和软件功能进行测试。“黑盒”法是穷举输入测试,只有把所有可能的输入都作为测试情况使用,才能以这种方法查出程序中所有的错误。实际上测试情况有无穷多个,人们不仅要测试所有合法的输入,而且还要对那些不合法但是可能的输入进行测试。 它不仅应用于开发阶段的测试,更重要的是在产品测试阶段及维护阶段必不可少。主要用于软件确认测试。 黑盒测试方法主要有:等价类划分边值分析因果图错误推测,白盒测试,白盒测试也称结构测试或逻辑驱动测试,它是知道产品内部工作过程,可通过测试来检测
7、产品内部动作是否按照规格说明书的规定正常进行,按照程序内部的结构测试程序,检验程序中的每条通路是否都有能按预定要求正确工作,而不顾它的功能。使用被测单元内部如何工作的信息,允许测试人员对程序内部逻辑结构及有关信息来设计和选择测试用例,对程序的逻辑路径进行测试。基于一个应用代码的内部逻辑知识,测试是基于覆盖全部代码、分支、路径、条件。,白盒测试的主要方法,逻辑驱动测试 基本路径测试主要用于软件验证。使用程序设计的控制结构导出测试用例。,逻辑驱动测试:主要是测试覆盖率,以程序内在逻辑结构为基础的测试。包括以下6种类型: 语句覆盖 判断覆盖 条件覆盖 判定-条件覆盖 条件组合覆盖 路径测试,白盒测试
8、的主要目的,保证一个模块中的所有独立路径至少被执行一次; 对所有的逻辑值均需要测试真、假两个分支; 在上下边界及可操作范围内运行所有循环; 检查内部数据结构以确保其有效性。,概念,语句覆盖:语句覆盖就是设计若干个测试用例,运行被测试程序,使得每一条可执行语句至少执行一次; 判定覆盖(也称为分支覆盖):设计若干个测试用例,运行所测程序,使程序中每个判断的取真分支和取假分支至少执行一次; 条件覆盖:设计足够多的测试用例,运行所测程序,使程序中每个判断的每个条件的每个可能取值至少执行一次; 判定-条件覆盖:设计足够多的测试用例,运行所测程序,使程序中每个判断的每个条件的所有可能取值至少执行一次,并且
9、每个可能的判断结果也至少执行一次,换句话说,即是要求各个判断的所有可能的条件取值组合至少执行一次; 条件组合测试:设计足够多的测试用例,运行所测程序,使程序中每个判断的所有可能的条件取值组合至少执行一次; 路径测试:设计足够多的测试用例,运行所测程序,要覆盖程序中所有可能的路径。,软件开发全过程检测及测试自动化,单元测试(unit test ) 由编程的开发人员自行计划与完成的,针对单个或相关联的一组程序单元的测试。 组装测试(inegration test )计划于设计阶段,由开发人员与测试人员合作完成的,针对结合起来的不同单元以及它们的接口的测试。 系统测试(system test ):(
10、可认为包括“可用性与图形用户界面测试”) 测试整个系统,以证实它满足要求所规定的功能、质量和性能等方面的特性。 回归测试(regression test ): 用于验证改变了的系统或其组件仍然保持应有的特性。 验收测试(acceptance test ): 测试整个系统,以保证其达到可以交付使用的状态。,V模型,V模型,单元测试、集成测试、系统测试、验收测试。是“从小到大”、“由内至外”、“循序渐进”的测试过程,体现了“分而治之”的思想。 单元测试的粒度最小,一般由开发小组采用白盒方式来测试,主要测试单元是否符合“设计”。 集成测试界于单元测试和系统测试之间,起到“桥梁作用”,一般由开发小组采
11、用白盒加黑盒的方式来测试,既要验证“设计”又要验证“需求”。,V模型,系统测试的粒度最大,一般由独立测试小组采用黑盒方式来测试,主要测试系统是否符合“需求规格说明书”。 验收测试与系统测试非常相似,主要区别是测试人员不同,验收测试由用户执行。,测试内容,测试内容一般包含 接口与路径测试。 功能测试、健壮性测试、性能测试、用户界面测试、安全性测试、压力测试、可靠性测试、安装/反安装测试,测试阶段对应表,接口与路径测试 3-1,接口测试:数据一般通过接口输入和输出,接口测试一般是白盒测试的第一步。输入参数有“典型值”、“边界值”、“异常值”输出包括函数的返回值和输出参数。实际输出与期望的输出不一致
12、,那么说明程序有错误。 一个函数体内的语句可能只有十几条,但逻辑路径可能有成千上万条。所以应该根据经验选择关键的路径测试。,接口与路径测试 3-2,路径测试的检查表 数据类型、变量值、逻辑判断、循环、内存管理、文件I/O、错误处理 预防一些重要的路径没有被测试的措施有: 观察是否有程序语句从来没有被执行过。 要特别留意函数体内的错误处理程序块。,接口与路径测试 3-3,接口与路径测试用例的参考模板,功能测试 3-1,功能测试的基本方法是构造一些合理输入(在需求范围之内),检查输出是否与期望的相同。如果两者不一致,即表明功能有误。 难点在于如何构造有效的输入。,功能测试 3-2,功能测试的测试方
13、法:等价划分法和边界值分析法。 等价划分是指把输入空间划分为几个“等价区间”,在每个“等价区间”中只需要测试一个典型值就可以了。等价划分法来源于人们的直觉与经验,可令测试事半功倍。 “缺陷遗漏在角落里,聚集在边界上”。边界值测试法是对等价划分法的补充。如果A和B是输入空间的边界值,那么除了典型值外还要用A和B作为测试用例。,功能测试 3-3,功能测试用例的参考模板,性能测试 3-1,性能测试即测试软件处理事务的速度,一是为了检验性能是否符合需求,二是为了得到某些性能数据供人们参考。 绝对值考虑:如数据送输速率是每秒多少比特。 “相对值”考虑:如某个软件比另一个软件快多少倍。 性能测试中考虑运行
14、环境的影响:例如网络环境、计算机主频,总线结构和外部设备都可能影响软件的运行速度。,性能测试 3-2,性能测试的一些注意事项: 应当编写一段程序用于计算时间以及相关数据。 应当测试软件在标准配置和最低配置下的性能。 应当关闭那些消耗内存、占用CPU的其它应用软件(如杀毒软件)。 应当分档记录。例如传输文件的容量从100K到1M可以分成若干等级。 同一种输入情况在不同的时间可能得到不同的性能数据,可以取其平均值。,性能测试 3-3,性能测试用例的参考模板,压力测试 2-1,压力测试也叫负荷测试,即获取系统能正常运行的极限状态。 压力测试的主要任务是:构造正确的输入,使劲折腾系统却让它刚好不瘫痪。
15、 压力测试的一个变种是敏感测试。在某种情况下,微小的输入变动会导致系统的表现(如性能)发生急剧的变化。,压力测试 2-2,压力测试用例的参考模板,其他测试内容,健壮性测试 用户界面测试 信息安全测试 可靠性测试 安装和反安装测试,问题1:有了“黑盒”测试为什么还要“白盒”测试? 问题2:由于单元测试要写测试驱动程序,非常麻烦,能否等到整个系统全部开发完后,再集中精力进行一次性地单元测试呢? 问题3:如果每个单元都通过了测试,把它们集成一起难道会有什么不妥吗?集成测试是否多此一举?,测试常见问题 2-1,测试常见问题 2-2,问题4:在集成测试的时候,已经对一些子系统进行了功能测试、性能测试等等
16、,那么在系统测试时能否跳过相同内容的测试? 问题5:既然系统测试与验收测试的内容几乎是相同的,为什么还要验收测试? 问题6:能否将系统测试和验收测试“合二为一”?,总结 2-1,测试可以将测试描述为一个运行程序以发现错误的过程。 软件测试的准则:不完全测试、风险测试、无法显示潜伏错误、发现错误成线性增长、缺陷不能完全修复、测试有条理规程 测试的方法:黑盒/白盒、静态/动态 软件测试的各个阶段:单元测试、集成测试、系统测试、验收测试,总结 2-2,测试的内容包括:接口/路径测试、功能测试、性能测试、压力测试、可靠性测试、安全性测试、用户界面测试、安装/反安装测试,X模型,测试驱动开发,TDD,
17、Test-Driven Development 测试驱动开发以测试作为开发过程的中心,它要求在编写任何代码之前,首先编写用于定义产品代码行为的测试,而编写的产品代码又以使测试通过为目标。测试驱动开发要求测试可以完全自动地运行,在代码进行重构前必须运行测试。,TDD基本做法,1. 写一个测试程序。 2. 让程序编译通过。 3. 运行测试程序,发现不能运行。 4. 让测试程序可以运行。 5. 消除重复设计,优化设计结构。,测试产品说明书,对于产品说明书的制定是个很重要的设计阶段,产品说明书的质量会直接影响到整个产品开发。 测试产品说明书属于静态黑盒子测试。,常用测试用语测试用例,测试用例:编写用于
18、输入输入的实际数制和预期结果。测试用例还明确指出使用具体测试用例产生的测试程序的任何限制 。 使用目的:测试用例应该设计为能够快速容易地发现尽可能多的错误。应该通过使用和产生正确和错误的输入和输出来“检验”程序。其目标是要使用合理范围内的条件,尽可能全面地测试所有模块乃至整个系统。,测试与调试什么是缺陷,缺陷:最终产品同用户的期望不一致 缺陷的分类 错误 遗漏 超出需求的部分 缺陷(未触发)VS.错误(应首先解决),测试与调试调试的准则,调试的方法:归纳法、演绎法和回溯法。 常用调试技术使用诊断输出语句 (diagnostic output statement)、快照转储 (snapshot
19、dump) 以及跟踪指令的断点 (instruction-dependent breakpoint)。,二、单元测试基本理论,什么是单元测试(Unit Test) 什么时候测试? 为什么要进行单元测试 C/C+单元测试问答(摘要),单元测试(Unit Test),工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。 临时单元测试:代码覆盖率要超过70%都很困难 充分的单元测试:提高软件质量,降低开发成本的必由之路。 单元测试是在软件开发过程中要进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。,单元测试(Unit Test),
20、对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。 在一种传统的结构化编程语言中,比如C,要进行测试的单元一般是函数或子过程。在象C+这样的面向对象的语言中,要进行测试的基本单元是类。对Ada语言来说,开发人员可以选择是在独立的过程和函数,还是在Ada包的级别上进行单元测试。单元测试的原则同样被扩展到第四代语言(4GL)的开发中,在这里基本单元被典型地划分为一个菜单或显示界面。,单元测试(Unit Test),单元测试不仅仅是作为无错编码一种辅助手段在一次性的开发过程中使用,单元测试必须是可重复的,无论是在软件修改,或是移植到新的运行环
21、境的过程中。因此,所有的测试都必须在整个软件系统的生命周期中进行维护。,三、为什么要 进行单元测试,一些流行的误解-反调论证 其他好处 单元测试的重要性,反证1:单元测试浪费了太多的时间,一旦编码完成,开发人员总是会迫切希望进行软件的集成工作,这样他们就能够看到实际的系统开始启动工作了。 这在外表上看来是一项明显的进步,而象单元测试这样的活动也许会被看作是通往这个阶段点的道路上的障碍, 推迟了对整个系统进行联调这种真正有意思的工作启动的时间。,反证1:单元测试浪费了太多的时间,在这种开发步骤中,真实意义上的进步被外表上的进步取代了。系统能够正常工作的可能性是很小的,更多的情况是充满了各式各样的
22、Bug。在实践中,这样一种开发步骤常常会导致这样的结果:软件甚至无法运行。更进一步的结果是大量的时间将被花费在跟踪那些包含在独立单元里的简单的Bug上面,在个别情况下,这些Bug也许是琐碎和微不足道的,但是总的来说,他们会导致在软件集成为一个系统时增加额外的工期, 而且当这个系统投入使用时也无法确保它能够可靠运行。,反证1:单元测试浪费了太多的时间,在实践工作中,进行了完整计划的单元测试和编写实际的代码所花费的精力大致上是相同的。一旦完成了这些单元测试工作,很多Bug将被纠正,在确信他们手头拥有稳定可靠的部件的情况下,开发人员能够进行更高效的系统集成工作。这才是真实意义上的进步,所以说完整计划
23、下的单元测试是对时间的更高效的利用。而调试人员的不受控和散漫的工作方式只会花费更多的时间而取得很少的好处。 使用AdaTEST和Cantata这样的支持工具可以使单元测试更加简单和有效。但这不是必须的,单元测试即使是在没有工具支持的情况下也是一项非常有意义的活动。,反证2:仅仅证明代码做了什么,这是那些没有首先为每个单元编写一个详细的规格说明而直接跳到编码阶段的开发人员提出的一条普遍的抱怨, 当编码完成以后并且面临代码测试任务的时候,他们就阅读这些代码并找出它实际上做了什么,把他们的测试工作基于已经写好的代码的基础上。当然,他们无法证明任何事情。所有的这些测试工作能够表明的事情就是编译器工作正
24、常。是的,他们也许能够抓住(希望能够)罕见的编译器Bug,但是他们能够做的仅仅是这些。,反证2:仅仅证明代码做了什么,如果他们首先写好一个详细的规格说明,测试能够以规格说明为基础。代码就能够针对它的规格说明,而不是针对自身进行测试。这样的测试仍然能够抓住编译器的Bug,同时也能找到更多的编码错误,甚至是一些规格说明中的错误。好的规格说明可以使测试的质量更高,所以最后的结论是高质量的测试需要高质量的规格说明。,反证2:仅仅证明代码做了什么,在实践中会出现这样的情况: 一个开发人员要面对测试一个单元时只给出单元的代码而没有规格说明这样吃力不讨好的任务。你怎样做才会有更多的收获,而不仅仅是发现编译器
25、的Bug?第一步是理解这个单元原本要做什么, - 不是它实际上做了什么。 比较有效的方法是倒推出一个概要的规格说明。这个过程的主要输入条件是要阅读那些程序代码和注释, 主要针对这个单元, 及调用它和被它调用的相关代码。画出流程图是非常有帮助的,你可以用手工或使用某种工具。 可以组织对这个概要规格说明的走读(Review),以确保对这个单元的说明没有基本的错误, 有了这种最小程度的代码深层说明,就可以用它来设计单元测试了。,反证3:我是个很棒的程序员, 我是不是可以不进行单元测试?,在每个开发组织中都至少有一个这样的开发人员,他非常擅长于编程,他们开发的软件总是在第一时间就可以正常运行,因此不需
26、要进行测试。你是否经常听到这样的借口?在真实世界里,每个人都会犯错误。即使某个开发人员可以抱着这种态度在很少的一些简单的程序中应付过去。 但真正的软件系统是非常复杂的。真正的软件系统不可以寄希望于没有进行广泛的测试和Bug修改过程就可以正常工作。编码不是一个可以一次性通过的过程。在真实世界中,软件产品必须进行维护以对操作需求的改变作出反应, 并且要对最初的开发工作遗留下来的Bug进行修改。你希望依靠那些原始作者进行修改吗? 这些制造出这些未经测试的原始代码的资深专家们还会继续在其他地方制造这样的代码。在开发人员做出修改后进行可重复的单元测试可以避免产生那些令人不快的负作用。,反证4:不管怎样,
27、 集成测试将会抓住所有的Bug ?,我们已经在前面的讨论中从一个侧面对这个问题进行了部分的阐述。这个论点不成立的原因在于规模越大的代码集成意味着复杂性就越高。如果软件的单元没有事先进行测试,开发人员很可能会花费大量的时间仅仅是为了使软件能够运行,而任何实际的测试方案都无法执行。一旦软件可以运行了,开发人员又要面对这样的问题: 在考虑软件全局复杂性的前提下对每个单元进行全面的测试。 这是一件非常困难的事情,甚至在创造一种单元调用的测试条件的时候,要全面的考虑单元的被调用时的各种入口参数。在软件集成阶段,对单元功能全面测试的复杂程度远远的超过独立进行的单元测试过程。最后的结果是测试将无法达到它所应
28、该有的全面性。一些缺陷将被遗漏,并且很多Bug将被忽略过去。让我们类比一下,假设我们要清洗一台已经完全装配好的食物加工机器!无论你喷了多少水和清洁剂,一些食物的小碎片还是会粘在机器的死角位置,只有任其腐烂并等待以后再想办法。但我们换个角度想想,如果这台机器是拆开的, 这些死角也许就不存在或者更容易接触到了,并且每一部分都可以毫不费力的进行清洗。,反证5:它的成本效率不高,一个特定的开发组织或软件应用系统的测试水平取决于对那些未发现的Bug的潜在后果的重视程度。这种后果的严重程度可以从一个Bug引起的小小的不便到发生多次的死机的情况。这种后果可能常常会被软件的开发人员所忽视(但是用户可不会这样)
29、,这种情况会长期的损害这些向用户提交带有Bug的软件的开发组织的信誉,并且会导致对未来的市场产生负面的影响。相反地,一个可靠的软件系统的良好的声誉将有助于一个开发组织获取未来的市场。 很多研究成果表明,无论什么时候作出修改都要进行完整的回归测试,在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。Bug发现的越晚,修改它所需的费用就越高,因此从经济角度来看, 应该尽可能早的查找和修改Bug。在修改费用变的过高之前,单元测试是一个在早期抓住Bug的机会。相比后阶段的测试,单元测试的创建更简单,维护更容易,并且可以更方便的进行重复。从全程的费用来考虑, 相比起那些复杂且旷日持久的集成
30、测试,或是不稳定的软件系统来说,单元测试所需的费用是很低的。,反证5:它的成本效率不高,摘自(Capers Jones,McGraw-Hill 1991),它列出了准备测试,执行测试,和修改缺陷所花费的时间(以一个功能点为基准),这些数据显示单元测试的成本效率大约是集成测试的两倍 系统测试的三倍。,反证5:它的成本效率不高,(术语域测试(Field test)意思是在软件投入使用以后,针对某个领域所作的所有测试活动)这个图表并不表示开发人员不应该进行后阶段的测试活动,这次测试活动仍然是必须的。它的真正意思是尽可能早的排除尽可能多的Bug可以减少后阶段测试的费用。其他的一些图表显示高达50%的维
31、护工作量被花在那些总是会有的Bug的修改上面。如果这些Bug在开发阶段被排除掉的话,这些工作量就可以节省下来。当考虑到软件维护费用可能会比最初的开发费用高出数倍的时候,这种潜在的对50%软件维护费用的节省将对整个软件生命周期费用产生重大的影响。,反证 结论,经验表明一个尽责的单元测试方法将会在软件开发的某个阶段发现很多的Bug,并且修改它们的成本也很低。在软件开发的后期阶段,Bug的发现并修改将会变得更加困难,并要消耗大量的时间和开发费用。无论什么时候作出修改都要进行完整的回归测试,在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。 在提供了经过测试的单元的情况下,系统集成过程
32、将会大大地简化。开发人员可以将精力集中在单元之间的交互作用和全局的功能实现上,而不是陷入充满很多Bug的单元之中不能自拔。 使测试工作的效力发挥到最大化的关键在于选择正确的测试策略,这其中包含了完全的单元测试的概念,以及对测试过程的良好的管理,还有适当地使用象AdaTEST和Cantata这样的工具来支持测试过程。这些活动可以产生这样的结果:在花费更低的开发费用的情况下得到更稳定的软件。更进一步的好处是简化了维护过程并降低了生命周期的费用。有效的单元测试是推行全局质量文化的一部分,而这种质量文化将会为软件开发者带来无限的商机。,其他好处 1:减少程序的Bug,要减少软件中的错误数目,方法之一就
33、是拥有一个专业的测试组,其工作就是尽一切可能使软件崩溃。不幸的是,如果拥有测试组,那么即使是经验丰富的开发人员,也会倾向于花费较少的时间来保证代码的可靠性。 软件界有一句俗语:“开发人员不应该测试他们自己的代码”。这是因为开发人员对自己的代码了如指掌,他们很清楚如何采用适当的方法对代码进行测试。尽管这句俗语很有道理,但却忽略了非常重要的一点 - 如果开发人员不对自己的代码进行测试,又如何知道代码能否按照预期的方式运行? 简单说来,他们根本无从得知。开发人员编写那种运行不正常或只在某些情况下运行正常的代码是一个严重的问题。他们通常只测试代码能否在很少的情况下正常运行,而不是验证代码能够在所有情况
34、下均正常运行。,其他好处 2:提高开发速度,在实践工作中,进行了完整计划的单元测试和编写实际的代码所花费的精力大致上是相同的。一旦完成了这些单元测试工作,很多Bug将被纠正,在确信他们手头拥有稳定可靠的部件的情况下,开发人员能够进行更高效的系统集成工作。这才是真实意义上的进步,所以说完整计划下的单元测试是对时间的更高效的利用。而调试人员的不受控和散漫的工作方式只会花费更多的时间而取得很少的好处。 经验表明一个尽责的单元测试方法将会在软件开发的某个阶段发现很多的Bug,并且修改它们的成本也很低。在软件开发的后期阶段,Bug的发现并修改将会变得更加困难,并要消耗大量的时间和开发费用。无论什么时候作
35、出修改都要进行完整的回归测试,在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。 在提供了经过测试的单元的情况下,系统集成过程将会大大地简化。开发人员可以将精力集中在单元之间的交互作用和全局的功能实现上,而不是陷入充满很多Bug的单元之中不能自拔。,其他好处 3:使程序代码更整洁,优化程序的设计,只有自动的单元测试程序失败时,我们才会去重写代码,在测试驱动开发中,要求我们对程序不停的重构,通过重构,我们可以优化程序的结构设计,消除程序中潜在的错误。同时,为了能够使自己的程序可以很方便的进行测试,开发人员就需要很好地考虑程序的设计,极限编程的方法说可以不需要设计就开始编码,但实际
36、上,它在编写代码的过程中每时每刻都为了方便的进行和通过测试而在优化自己的设计。它实际上是把开始阶段很大很抽象的设计分散到你编写的每个方法中。因此他们会说好设计最后会自然而然的出现。,其他好处 4:编写单元测试代码的过程实际上就是设计程序的过程,在编写单元测试代码时,我们实际上是在思考我们的程序根据预期会返回什么结果,它实际上就是程序设计的过程。而通过重构过程,我们可以对这些设计进行很好的优化。,单元测试的重要性,单元测试是软件测试的基础,因此单元测试的效果会直接影响到软件的后期测试,最终在很大程度上影响到产品的质量。 时间方面 测试效果 测试成本 产品质量,重要性 1:时间方面,如果认真的做好
37、了单元测试,在系统集成联调时非常顺利,因此会节约很多时间,反之那些由于因为时间原因不做单元测试或随便做做的则在集成时总会遇到那些本应该在单元测试就能发现的问题,而这种问题在集成时遇到往往很难让开发人员预料到,最后在苦苦寻觅中才发现这是个很低级的错误而在悔恨自己时已经浪费了很多时间,这种时间上的浪费一点都不值得,正所谓得不偿失。,重要性 2:测试效果,根据以往的测试经验来看,单元测试的效果是非常明显的,首先它是测试阶段的基础,做好了单元测试,在做后期的集成测试和系统测试时就很顺利。其次在单元测试过程中能发现一些很深层次的问题,同时还会发现一些很容易发现而在集成测试和系统测试很难发现的问题。再次单
38、元测试关注的范围也特殊,它不仅仅是证明这些代码做了什么,最重要的是代码是如何做的,是否做了它该做的事情而没有做不该做的事情。,重要性 3:测试成本,在单元测试时某些问题就很容易发现,如果在后期的测试中发现问题所花的成本将成倍数上升。比如在单元测试时发现1个问题需要1个小时,则在集成测试时发现该问题需要2个小时,在系统测试时发现则需要3个小时,同理还有定位问题和解决问题的费用也是成倍数上升的,这就是我们要尽可能早的排除尽可能多的bug来减少后期成本的因素之一。,重要性 4:产品质量,单元测试的好与坏直接影响到产品的质量,可能就是由于代码中的某一个小错误就导致了整个产品的质量降低一个指标,或者导致
39、更严重的后果,如果我们做好了单元测试这种情况是可以完全避免的。 综上所述,单元测试是构筑产品质量的基石,我们不要因为节约单元测试的时间不做单元测试或随便做而让我们在后期浪费太多的不值得的时间,我们也不愿意因为由于节约那些时间导致开发出来的整个产品失败或重来!,四、 C/C+单元测试问答,为什么要进行单元测试? 由谁进行测试?开发部门还是测试部门? 由测试部门进行单元测试为什么成本昂贵? 由开发部门进行单元测试能保证测试效果吗? 边编码边测试会影响编码进度吗? 实施单元测试需要改变开发流程吗? 单元测试测试哪些代码? 实际工作中,单元测试能实现什么程度的测试覆盖? 单元测试如何改良项目代码的整体
40、结构? 我希望依赖全自动的工具来完成单元测试,这一想法现实吗? 如果由开发部门实施单元测试,那么测试部门要做哪些工作?,http:/www.KaileS,为什么要进行单元测试?,单元测试保证局部代码的质量 单元测试改良项目代码的整体结构 单元测试降低测试、维护升级的成本 单元测试使开发过程适应频繁变化的需求 单元测试有助于提升程序员的能力,由谁进行测试?开发部门/测试部门?,应该由开发部门进行单元测试! 由测试部门进行单元测试的问题:代价高,人手不足,耽误了测试部门对其他测试的准备工作。 由开发部门进行单元测试的问题:担心影响开发进度,程序员不习惯做单元测试,测试自己编写的代码,难于保证测试的
41、效果。 无论由哪个部门做单元测试,都要面对一些问题,但开发部门所面对的问题可以借助工具来解决,而由测试部门进行单元测试,要么无法真正实施,要么代价昂贵。,由测试部门来单元测试成本昂贵?,需多次重复理解程序 反复沟通需要大量时间成本 不利于发挥单元测试对代码结构的约束机制 耽误测试部门对其他测试的准备工作 即使测试部门人手充裕,仅仅从效益来考虑,也不应该由测试部门进行单元测试。如果测试部门本来就人力不充裕(进行单元测试的人员需具备编码能力),勉强由测试部门进行单元测试,结果往往是-没有结果。,由开发部门进行单元测试能保证测试效果吗?,程序员测试自己编写的代码,往往只考虑“正常状况”,这当然会影响
42、测试效果。但如果所用的单元测试工具能够统计各种白盒覆盖率,就能检查测试效果。当然,只做到这一点还是不够的,因为白盒覆盖具有逾后逾难的特点,达到一定的覆盖率后,覆盖率的提升会很困难。如果测试工具功能足够强大,能提供工具帮助用户快速地设计测试用例,达到完整的白盒覆盖,那么测试效果就能得到完全的保证。 实际上,如果没有充分的统计数据,没有达到足够的测试完整性,那么由谁做单元测试,效果都不能保证。,边编码边测试会影响编码进度吗?,传统的单元测试是很费时费力的工作,主要时间消耗在于:编写测试代码、设计测试用例,如果开发工具能自动生成测试代码,并且具有快速设计测试用例的功能,那么测试费时就很少;另一方面,
43、如果测试工具还能提供数据,帮助程序员整理编程思路、快速发现错误,更高效地调试,那么就能大量提高开发效率,抵销测试所消耗的时间,不但不会影响编码进度,甚至加快编码进度。,实施单元测试需要改变开发流程吗?,边开发边测试,单元测试是编码行为而不是测试行为,测试代码看作是项目代码的一部分,程序员提交产品代码时也要提交测试代码和测试报告,其他流程可以不作任何改变。 另一方面,在充分单元测试的基础上,由于具有高质量的局部代码,良好的整体代码结构,保证了代码的可扩展性和可复用性,同时,自动回归测试支持对代码的频繁修改而不用担心引入新的错误,因此,开发流程自然会变得敏捷,可以适应频繁变化的需求,使系统分析、架
44、构设计和后期测试的压力减轻,自然而有效地改进了开发流程。,单元测试测试哪些代码?,单元测试通常不测试很简单的代码,一般也不测试“边界代码”。很简单的代码容易理解,例如Get/Set函数,这里解释一下“边界代码”。“边界代码”是指用于与外部系统交互的代码,例如用于处理用户界面的代码。数据库、文件、网络都可以看作是外部系统,用于读写数据库或文件、或访问网络的代码也可以看作是“边界代码”,这类代码应该独立出来,可以进行单元测试,但对这些代码的单元测试通常不能自动验证预期输出,而是需要人工察看。编程时,不要把普通代码与“边界代码”混在一起,例如,不要把各种运算直接写在界面类中,做到了这一点,绝大多数代
45、码都可以进行单元测试。,实际工作中,单元测试能实现什么程度的测试覆盖?,单元测试的最低要求是100%语句覆盖,这个覆盖率还是不够的,最好实现多种覆盖的组全,比较理想的覆盖率组合是:100%的语句、条件、分支、路径覆盖,另外,测试工具最好还能自动生成边界测试用例捕捉未处理特殊输入形成的错误。在达到这种覆盖之后,残留的编码错误可以几乎说没有了(设计方面的错误除外,这些属于集成或系统测试的范畴)。,单元测试如何改良项目代码的整体结构?,具有良好整体结构的代码,应该符合“低耦合”的特性,即具有“可测性”。测试不具有“可测性”的代码时一般会产生编译错误,或者需要打桩才能测试,从而将问题暴露出来。发现问题
46、后,重构代码、消除不当耦合一般不难,这种简单的重构将有效地改良代码的整体结构。,我希望依赖全自动的工具来完成单元测试,这一想法现实吗?,完全自动化是一个美妙的愿望,但由于单元测试的基本特性,完全自动化的单元测试是不现实的。 与其他不同,单元测试是“隔离”的测试,要求代码具有可测性,一个项目甚至一个文件中,难免会有些影响可测性的代码,编译到这些代码时常常会产生编译错误,因此,全自动的单元测试工具往往只能测试小部分代码,即使使用某种技术手段屏蔽掉编译错误,也得不偿失,因为同时也屏蔽掉了改良代码整体结构的宝贵机会。如果采用自底向上的方式,一个一个文件测试,测试一个文件前,先将该文件加入测试工程并编译
47、,没有编译错误时再测试,这样可以及时发现并消除不当耦合,使代码具有可测性,这种非全自动的方式,可以测试绝大多数代码,也保证了代码具有良好的整体结构。 另一方面,主要由测试工具自动生成测试用例来进行测试往往没有实际意义,因为测试工具无法自动了解程序的功能,因此,自动测试用例通常只能发现异常之类的极端错误,大多数一般错误都是无法发现的。测试工具最重要的不是自动生成测试用例,而是能提供快速建立和编辑测试用例的工具。,如果由开发部门实施单元测试,那么测试部门要做哪些工作?,推动、组织单元测试的实施。单元测试既然叫做“测试”,开发部门常常认识不到其重要性和必要性,需要由测试部门推动和协助组织实施。 制定
48、单元测试规范,培训单元测试技术。 检查、审核单元测试结果,保证单元测试的有效性。,五、单元测试工具,测试工具的分类和选择 Parasoft Compuware Rational AutomatedQA AQTime xUnit系列 Visual Studio 2005 Visual Unit,测试工具的分类和选择,白盒测试工具 静态测试工具 动态测试工具 黑盒测试工具 功能测试工具 性能测试工具 测试管理工具 其他测试工具 测试工具的选择,白盒测试工具,白盒测试工具一般是针对代码进行测试,测试中发现的缺陷可以定位到代码级 静态测试工具直接对代码进行分析,不需要运行代码,也不需要对代码编译链接,
49、生成可执行文件。静态测试工具一般是对代码进行语法扫描,找出不符合编码规范的地方,根据某种质量模型评价代码的质量,生成系统的调用关系图等。 动态测试工具与静态测试工具不同,动态测试工具的一般采用“插桩”的方式,向代码生成的可执行文件中插入一些监测代码,用来统计程序运行时的数据。其与静态测试工具最大的不同就是动态测试工具要求被测系统实际运行。,黑盒测试工具,黑盒测试工具适用于黑盒测试的场合,黑盒测试工具包括功能测试工具和性能测试工具。 黑盒测试工具的一般原理是利用脚本的录制(Record)/回放(Playback),模拟用户的操作,然后将被测系统的输出记录下来同预先给定的标准结果比较。黑盒测试工具
50、可以大大减轻黑盒测试的工作量,在迭代开发的过程中,能够很好地进行回归测试。 黑盒测试工具的代表有Rational公司的TeamTest、Robot,Compuware公司的QACenter,另外,专用于性能测试的工具包括有Radview公司的WebLoad、Microsoft公司的WebStress等工具。,测试管理工具,测试管理工具用于对测试进行管理。 内容:对测试计划、测试用例、测试实施进行管理,对缺陷的跟踪管理。 测试管理工具的代表有Rational公司的Test Manager、Compureware公司的TrackRecord,Mercury的TestDirector和Quality Center等软件。,