日历
| |||||||||
| 日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
| 1 | |||||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||
| 9 | 10 | 11 | 12 | 13 | 14 | 15 | |||
| 16 | 17 | 18 | 19 | 20 | 21 | 22 | |||
| 23 | 24 | 25 | 26 | 27 | 28 | 29 | |||
| 30 | |||||||||
我的好友
统计信息
- 访问量: 1680
- 日志数: 23
- 书签数: 1
- 建立时间: 2007-06-30
- 更新时间: 2007-12-16
我的最新日志
-
边测边开心
2007-12-16
在51的日子是充实快乐的,这几天的体会更加深刻~~
每周又加了一天的课,这倒也没什么,学习的内容接触到具体的项目有时候无从下手,面临找工作,压力又加大了...有时候对于一个问题没有一点思路的时候,真的很郁闷
不过还好,大家在一起都互相帮助,聊聊天,开开玩笑,蛮开心的...踏踏实实做好每一件事情,老师布置的任务我可能不能按时交上去,但也绝不会应付一下了事,做的慢没关系,如果应付一下说明态度问题,这个问题就严重去了。所以我的原则是:认认真真做每件事情,只有这样去做才能学的扎实。
随便发发感慨,第一阶段的理论部分基本讲完了,后面项目实践和工具会多一些
-
(zz)单元测试
2007-12-06
这篇文章首先讲的很详细,再者讲的东西和上课的内容有些很相似,就是有点长
单元测试
单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。例如,你可能把一个很大的值放入一个有序list 中去,然后确认该值出现在list 的尾部。或者,你可能会从字符串中删除匹配某种模式的字符,然后确认字符串确实不再包含这些字符了。
单元测试是由程序员自己来完成,最终受益的也是程序员自己。可以这么说,程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试。执行单元测试,就是为了证明这段代码的行为和我们期望的一致。
工厂在组装一台电视机之前,会对每个元件都进行测试,这,就是单元测试。
其实我们每天都在做单元测试。你写了一个函数,除了极简单的外,总是要执行一下,看看功能是否正常,有时还要想办法输出些数据,如弹出信息窗口什么的,这,也是单元测试,老纳把这种单元测试称为临时单元测试。只进行了临时单元测试的软件,针对代码的测试很不完整,代码覆盖率要超过70%都很困难,未覆盖的代码可能遗留大量的细小的错误,这些错误还会互相影响,当BUG暴露出来的时候难于调试,大幅度提高后期测试和维护成本,也降低了开发商的竞争力。可以说,进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。
对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。
要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。老纳认为,比较简单的办法是为产品工程建立对应的测试工程,为每个类建立对应的测试类,为每个函数(很简单的除外)建立测试函数。首先就几个概念谈谈老纳的看法。
一般认为,在结构化程序时代,单元测试所说的单元是指函数,在当今的面向对象时代,单元测试所说的单元是指类。以老纳的实践来看,以类作为测试单位,复杂度高,可操作性较差,因此仍然主张以函数作为单元测试的测试单位,但可以用一个测试类来组织某个类的所有测试函数。单元测试不应过分强调面向对象,因为局部代码依然是结构化的。单元测试的工作量较大,简单实用高效才是硬道理。
有一种看法是,只测试类的接口(公有函数),不测试其他函数,从面向对象角度来看,确实有其道理,但是,测试的目的是找错并最终排错,因此,只要是包含错误的可能性较大的函数都要测试,跟函数是否私有没有关系。对于C++来说,可以用一种简单的方法区隔需测试的函数:简单的函数如数据读写函数的实现在头文件中编写(inline函数),所有在源文件编写实现的函数都要进行测试(构造函数和析构函数除外)。
为什么要使用单元测试
我们编写代码时,一定会反复调试保证它能够编译通过。如果是编译没有通过的代码,没有任何人会愿意交付给自己的老板。但代码通过编译,只是说明了它的语法正确;我们却无法保证它的语义也一定正确,没有任何人可以轻易承诺这段代码的行为一定是正确的。
幸运,单元测试会为我们的承诺做保证。编写单元测试就是用来验证这段代码的行为是否与我们期望的一致。有了单元测试,我们可以自信的交付自己的代码,而没有任何的后顾之忧。
什么时候测试?单元测试越早越好,早到什么程度?XP开发理论讲究TDD,即测试驱动开发,先编写测试代码,再进行开发。在实际的工作中,可以不必过分强调先什么后什么,重要的是高效和感觉舒适。从老纳的经验来看,先编写产品函数的框架,然后编写测试函数,针对产品函数的功能编写测试用例,然后编写产品函数的代码,每写一个功能点都运行测试,随时补充测试用例。所谓先编写产品函数的框架,是指先编写函数空的实现,有返回值的随便返回一个值,编译通过后再编写测试代码,这时,函数名、参数表、返回类型都应该确定下来了,所编写的测试代码以后需修改的可能性比较小。
由谁测试?单元测试与其他测试不同,单元测试可看作是编码工作的一部分,应该由程序员完成,也就是说,经过了单元测试的代码才是已完成的代码,提交产品代码时也要同时提交测试代码。测试部门可以作一定程度的审核。
关于桩代码,老纳认为,单元测试应避免编写桩代码。桩代码就是用来代替某些代码的代码,例如,产品函数或测试函数调用了一个未编写的函数,可以编写桩函数来代替该被调用的函数,桩代码也用于实现测试隔离。采用由底向上的方式进行开发,底层的代码先开发并先测试,可以避免编写桩代码,这样做的好处有:减少了工作量;测试上层函数时,也是对下层函数的间接测试;当下层函数修改时,通过回归测试可以确认修改是否导致上层函数产生错误。
在一种传统的结构化编程语言中,比如C,要进行测试的单元一般是函数或子过程。在象C++这样的面向对象的语言中, 要进行测试的基本单元是类。对Ada语言来说,开发人员可以选择是在独立的过程和函数,还是在Ada包的级别上进行单元测试。单元测试的原则同样被扩展到第四代语言(4GL)的开发中,在这里基本单元被典型地划分为一个菜单或显示界面。
单元测试不仅仅是作为无错编码一种辅助手段在一次性的开发过程中使用,单元测试必须是可重复的,无论是在软件修改,或是移植到新的运行环境的过程中。因此,所有的测试都必须在整个软件系统的生命周期中进行维护。
经常与单元测试联系起来的另外一些开发活动包括代码走读(Code review),静态分析(Static analysis)和动态分析(Dynamic analysis)。静态分析就是对软件的源代码进行研读,查找错误或收集一些度量数据,并不需要对代码进行编译和执行。动态分析就是通过观察软件运行时的动作,来提供执行跟踪,时间分析,以及测试覆盖度方面的信息。
一些流行的误解
在明确了什么是单元测试以后,我们可以进行"反调论证"了。在下面的章节里,我们列出了一些反对单元测试的普遍的论点。然后用充分的理由来证明这些论点是不足取的。
它浪费了太多的时间
一旦编码完成,开发人员总是会迫切希望进行软件的集成工作,这样他们就能够看到实际的系统开始启动工作了。 这在外表上看来是一项明显的进步,而象单元测试这样的活动也许会被看作是通往这个阶段点的道路上的障碍, 推迟了对整个系统进行联调这种真正有意思的工作启动的时间。在这种开发步骤中,真实意义上的进步被外表上的进步取代了。系统能够正常工作的可能性是很小的,更多的情况是充满了各式各样的Bug。在实践中,这样一种开发步骤常常会导致这样的结果:软件甚至无法运行。更进一步的结果是大量的时间将被花费在跟踪那些包含在独立单元里的简单的Bug上面,在个别情况下,这些Bug也许是琐碎和微不足道的,但是总的来说,他们会导致在软件集成为一个系统时增加额外的工期, 而且当这个系统投入使用时也无法确保它能够可靠运行。
在实践工作中,进行了完整计划的单元测试和编写实际的代码所花费的精力大致上是相同的。一旦完成了这些单元测试工作,很多Bug将被纠正,在确信他们手头拥有稳定可靠的部件的情况下,开发人员能够进行更高效的系统集成工作。这才是真实意义上的进步,所以说完整计划下的单元测试是对时间的更高效的利用。而调试人员的不受控和散漫的工作方式只会花费更多的时间而取得很少的好处。
使用AdaTEST和Cantata这样的支持工具可以使单元测试更加简单和有效。但这不是必须的,单元测试即使是在没有工具支持的情况下也是一项非常有意义的活动。
它仅仅是证明这些代码做了什么
这是那些没有首先为每个单元编写一个详细的规格说明而直接跳到编码阶段的开发人员提出的一条普遍的抱怨, 当编码完成以后并且面临代码测试任务的时候,他们就阅读这些代码并找出它实际上做了什么,把他们的测试工作基于已经写好的代码的基础上。当然,他们无法证明任何事情。所有的这些测试工作能够表明的事情就是编译器工作正常。是的,他们也许能够抓住(希望能够)罕见的编译器Bug,但是他们能够做的仅仅是这些。如果他们首先写好一个详细的规格说明,测试能够以规格说明为基础。代码就能够针对它的规格说明,而不是针对自身进行测试。这样的测试仍然能够抓住编译器的Bug,同时也能找到更多的编码错误,甚至是一些规格说明中的错误。好的规格说明可以使测试的质量更高,所以最后的结论是高质量的测试需要高质量的规格说明。
在实践中会出现这样的情况: 一个开发人员要面对测试一个单元时只给出单元的代码而没有规格说明这样吃力不讨好的任务。你怎样做才会有更多的收获,而不仅仅是发现编译器的Bug?第一步是理解这个单元原本要做什么, --- 不是它实际上做了什么。 比较有效的方法是倒推出一个概要的规格说明。这个过程的主要输入条件是要阅读那些程序代码和注释, 主要针对这个单元, 及调用它和被它调用的相关代码。画出流程图是非常有帮助的,你可以用手工或使用某种工具。 可以组织对这个概要规格说明的走读(Review),以确保对这个单元的说明没有基本的错误, 有了这种最小程度的代码深层说明,就可以用它来设计单元测试了。
我是个很棒的程序员, 我是不是可以不进行单元测试?
在每个开发组织中都至少有一个这样的开发人员,他非常擅长于编程,他们开发的软件总是在第一时间就可以正常运行,因此不需要进行测试。你是否经常听到这样的借口?在真实世界里,每个人都会犯错误。即使某个开发人员可以抱着这种态度在很少的一些简单的程序中应付过去。 但真正的软件系统是非常复杂的。真正的软件系统不可以寄希望于没有进行广泛的测试和Bug修改过程就可以正常工作。
编码不是一个可以一次性通过的过程。在真实世界中,软件产品必须进行维护以对操作需求的改变作出反应, 并且要对最初的开发工作遗留下来的Bug进行修改。你希望依靠那些原始作者进行修改吗? 这些制造出这些未经测试的原始代码的资深专家们还会继续在其他地方制造这样的代码。在开发人员做出修改后进行可重复的单元测试可以避免产生那些令人不快的负作用。
不管怎样, 集成测试将会抓住所有的Bug
我们已经在前面的讨论中从一个侧面对这个问题进行了部分的阐述。这个论点不成立的原因在于规模越大的代码集成意味着复杂性就越高。如果软件的单元没有事先进行测试,开发人员很可能会花费大量的时间仅仅是为了使软件能够运行,而任何实际的测试方案都无法执行。一旦软件可以运行了,开发人员又要面对这样的问题: 在考虑软件全局复杂性的前提下对每个单元进行全面的测试。 这是一件非常困难的事情,甚至在创造一种单元调用的测试条件的时候,要全面的考虑单元的被调用时的各种入口参数。在软件集成阶段,对单元功能全面测试的复杂程度远远的超过独立进行的单元测试过程。
最后的结果是测试将无法达到它所应该有的全面性。一些缺陷将被遗漏,并且很多Bug将被忽略过去。
让我们类比一下,假设我们要清洗一台已经完全装配好的食物加工机器!无论你喷了多少水和清洁剂,一些食物的小碎片还是会粘在机器的死角位置,只有任其腐烂并等待以后再想办法。但我们换个角度想想,如果这台机器是拆开的, 这些死角也许就不存在或者更容易接触到了,并且每一部分都可以毫不费力的进行清洗。
它的成本效率不高
一个特定的开发组织或软件应用系统的测试水平取决于对那些未发现的Bug的潜在后果的重视程度。这种后果的严重程度可以从一个Bug引起的小小的不便到发生多次的死机的情况。这种后果可能常常会被软件的开发人员所忽视(但是用户可不会这样),这种情况会长期的损害这些向用户提交带有Bug的软件的开发组织的信誉,并且会导致对未来的市场产生负面的影响。相反地,一个可靠的软件系统的良好的声誉将有助于一个开发组织获取未来的市场。很多研究成果表明,无论什么时候作出修改都要进行完整的回归测试,在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。Bug发现的越晚,修改它所需的费用就越高,因此从经济角度来看, 应该尽可能早的查找和修改Bug。在修改费用变的过高之前,单元测试是一个在早期抓住Bug的机会。
相比后阶段的测试,单元测试的创建更简单,维护更容易,并且可以更方便的进行重复。从全程的费用来考虑, 相比起那些复杂且旷日持久的集成测试,或是不稳定的软件系统来说,单元测试所需的费用是很低的。
一些图表
这些图表摘自<<实用软件度量>>(Capers Jones,McGraw-Hill 1991),它列出了准备测试,执行测试,和修改缺陷所花费的时间(以一个功能点为基准),这些数据显示单元测试的成本效率大约是集成测试的两倍 系统测试的三倍(参见条形图)。(术语域测试(Field test)意思是在软件投入使用以后,针对某个领域所作的所有测试活动)
这个图表并不表示开发人员不应该进行后阶段的测试活动,这次测试活动仍然是必须的。它的真正意思是尽可能早的排除尽可能多的Bug可以减少后阶段测试的费用。
其他的一些图表显示高达50%的维护工作量被花在那些总是会有的Bug的修改上面。如果这些Bug在开发阶段被排除掉的话,这些工作量就可以节省下来。当考虑到软件维护费用可能会比最初的开发费用高出数倍的时候,这种潜在的对50%软件维护费用的节省将对整个软件生命周期费用产生重大的影响。
结论
经验表明一个尽责的单元测试方法将会在软件开发的某个阶段发现很多的Bug,并且修改它们的成本也很低。在软件开发的后期阶段,Bug的发现并修改将会变得更加困难,并要消耗大量的时间和开发费用。无论什么时候作出修改都要进行完整的回归测试,在生命周期中尽早地对软件产品进行测试将使效率和质量得到最好的保证。 在提供了经过测试的单元的情况下,系统集成过程将会大大地简化。开发人员可以将精力集中在单元之间的交互作用和全局的功能实现上,而不是陷入充满很多Bug的单元之中不能自拔。
使测试工作的效力发挥到最大化的关键在于选择正确的测试策略,这其中包含了完全的单元测试的概念,以及对测试过程的良好的管理,还有适当地使用象AdaTEST和Cantata这样的工具来支持测试过程。这些活动可以产生这样的结果:在花费更低的开发费用的情况下得到更稳定的软件。更进一步的好处是简化了维护过程并降低了生命周期的费用。有效的单元测试是推行全局质量文化的一部分,而这种质量文化将会为软件开发者带来无限的商机。
单元测试的优点
1、它是一种验证行为。
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支缓。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。2、它是一种设计行为。
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。3、它是一种编写文档的行为。
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。4、它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。单元测试的范畴
如果要给单元测试定义一个明确的范畴,指出哪些功能是属于单元测试,这似乎很难。但下面讨论的四个问题,基本上可以说明单元测试的范畴,单元测试所要做的工作。1、 它的行为和我期望的一致吗?
这是单元测试最根本的目的,我们就是用单元测试的代码来证明它所做的就是我们所期望的。2、 它的行为一直和我期望的一致吗?
编写单元测试,如果只测试代码的一条正确路径,让它正确走一遍,并不算是真正的完成。软件开发是一个项复杂的工程,在测试某段代码的行为是否和你的期望一致时,你需要确认:在任何情况下,这段代码是否都和你的期望一致;譬如参数很可疑、硬盘没有剩余空间、缓冲区溢出、网络掉线的时候。3、 我可以依赖单元测试吗?
不能依赖的代码是没有多大用处的。既然单元测试是用来保证代码的正确性,那么单元测试也一定要值得依赖。4、 单元测试说明我的意图了吗?
单元测试能够帮我们充分了解代码的用法,从效果上而言,单元测试就像是能执行的文档,说明了在你用各种条件调用代码时,你所能期望这段代码完成的功能。不写测试的借口
到这里,我们已经列举了使用单元测试的种种理由。也许,每个人都同意,是的,该做更多的测试。这种人人同意的事情还多着呢,是的,该多吃蔬菜,该戒烟,该多休息,该多锻炼……这并不意味着我们中的所有人都会这么去做,不是吗?1、 编写单元测试太花时间了。
我们知道,在开发时越早发现BUG,就能节省更多的时间,降低更多的风险。
下图表摘自<<实用软件度量>>(Capers Jones,McGraw-Hill 1991),它列出了准备测试,执行测试,和修改缺陷所花费的时间(以一个功能点为基准),这些数据显示单元测试的成本效率大约是集成测试的两倍,是系统测试的三倍(参见条形图)。术语:域测试(Field test)意思是在软件投入使用以后,针对某个领域所作的所有测试活动。
如果你仍然认为在编写产品代码的时候,还是没有时间编写测试代码,那么请先考虑下面这些问题:
1)、对于所编写的代码,你在调试上面花了多少时间。
2)、对于以前你自认为正确的代码,而实际上这些代码却存在重大的bug,你花了多少时间在重新确认这些代码上面。
3)、对于一个别人报告的bug,你花了多少时间才找出导致这个bug 的源码位置。
回答完这些问题,你一定不再以“太花时间”作为拒绝单元测试的借口。2、 运行测试的时间太长了。
合适的测试是不会让这种情况发生的。实际上,大多数测试的执行都是非常快的,因此你在几秒之内就可以运行成千上万个测试。但是有时某些测试会花费很长的时间。这时,需要把这些耗时的测试和其他测试分开。通常可以每天运行这种测试一次,或者几天一次。3、 测试代码并不是我的工作。
你的工作就是保证代码能够正确的完成你的行为,恰恰相反,测试代码正是你不可缺少的工作。4、 我并不清楚代码的行为,所以也就无从测试。
如果你实在不清楚代码的行为,那么估计现在并不是编码的时候。如果你并不知道代码的行为,那么你又如何知道你编写的代码是正确的呢?5、 但是这些代码都能够编译通过。
我们前面已经说过,代码通过编译只是验证它的语法通过。但并不能保证它的行为就一定正确。6、 公司请我来是为了写代码,而不是写测试。
公司付给你薪水是为了让你编写产品代码,而单元测试大体上是一个工具,是一个和编辑器、开发环境、编译器等处于同一位置的工具。7、 如果我让测试员或者QA(Quality Assurance)人员没有工作,那么我会觉得很内疚。
你并不需要担心这些。请记住,我们在此只是谈论单元测试,而它只是一种针对源码的、低层次的,为程序员而设计的测试。在整个项目中,还有其他的很多测试需要这些人来完成,如:功能测试、验收测试、性能测试、环境测试、有效性测试、正确性测试、正规分析等等。
8、 我的公司并不会让我在真实系统中运行单元测试。
我们所讨论的只是针对开发者的单元测试。也就是说,如果你可以在其他的环境下(例如在正式的产品系统中)运行这些测试的话,那么它们就不再是单元测试,而是其他类型的测试了。实际上,你可以在你的本机运行单元测试,使用你自己的数据库,或者使用mock 对象。
测试代码编写
多数讲述单元测试的文章都是以Java为例,本文以C++为例,后半部分所介绍的单元测试工具也只介绍C++单元测试工具。下面的示例代码的开发环境是VC6.0。产品类:
class CMyClass
{
public:
int Add(int i, int j);
CMyClass();
virtual ~CMyClass();private:
int mAge; //年龄
CString mPhase; //年龄阶段,如"少年","青年"
};建立对应的测试类CMyClassTester,为了节约编幅,只列出源文件的代码:
void CMyClassTester::CaseBegin()
{
//pObj是CMyClassTester类的成员变量,是被测试类的对象的指针,
//为求简单,所有的测试类都可以用pObj命名被测试对象的指针。
pObj = new CMyClass();
}void CMyClassTester::CaseEnd()
{
delete pObj;
}
测试类的函数CaseBegin()和CaseEnd()建立和销毁被测试对象,每个测试用例的开头都要调用CaseBegin(),结尾都要调用CaseEnd()。接下来,我们建立示例的产品函数:
int CMyClass::Add(int i, int j)
{
return i+j;
}
和对应的测试函数:
void CMyClassTester::Add_int_int()
{
}
把参数表作为函数名的一部分,这样当出现重载的被测试函数时,测试函数不会产生命名冲突。下面添加测试用例:
void CMyClassTester::Add_int_int()
{
//第一个测试用例
CaseBegin();{ //1
int i = 0; //2
int j = 0; //3
int ret = pObj->Add(i, j); //4
ASSERT(ret == 0); //5
}CaseEnd(); //6
}
第1和第6行建立和销毁被测试对象,所加的{}是为了让每个测试用例的代码有一个独立的域,以便多个测试用例使用相同的变量名。第2和第3行是定义输入数据,第4行是调用被测试函数,这些容易理解,不作进一步解释。第5行是预期输出,它的特点是当实际输出与预期输出不同时自动报错,ASSERT是VC的断言宏,也可以使用其他类似功能的宏,使用测试工具进行单元测试时,可以使用该工具定义的断言宏。
示例中的格式显得很不简洁,2、3、4、5行可以合写为一行:ASSERT(pObj->Add(0, 0) == 0);但这种不简洁的格式却是老纳极力推荐的,因为它一目了然,易于建立多个测试用例,并且具有很好的适应性,同时,也是极佳的代码文档,总之,老纳建议:输入数据和预期输出要自成一块。
建立了第一个测试用例后,应编译并运行测试,以排除语法错误,然后,使用拷贝/修改的办法建立其他测试用例。由于各个测试用例之间的差别往往很小,通常只需修改一两个数据,拷贝/修改是建立多个测试用例的最快捷办法。
下面说说测试用例、输入数据及预期输出。输入数据是测试用例的核心,老纳对输入数据的定义是:被测试函数所读取的外部数据及这些数据的初始值。外部数据是对于被测试函数来说的,实际上就是除了局部变量以外的其他数据,老纳把这些数据分为几类:参数、成员变量、全局变量、IO媒体。IO媒体是指文件、数据库或其他储存或传输数据的媒体,例如,被测试函数要从文件或数据库读取数据,那么,文件或数据库中的原始数据也属于输入数据。一个函数无论多复杂,都无非是对这几类数据的读取、计算和写入。预期输出是指:返回值及被测试函数所写入的外部数据的结果值。返回值就不用说了,被测试函数进行了写操作的参数(输出参数)、成员变量、全局变量、IO媒体,它们的预期的结果值都是预期输出。一个测试用例,就是设定输入数据,运行被测试函数,然后判断实际输出是否符合预期。下面举一个与成员变量有关的例子:
产品函数:
void CMyClass::Grow(int years)
{
mAge += years;if(mAge < 10)
mPhase = "儿童";
else if(mAge <20)
mPhase = "少年";
else if(mAge <45)
mPhase = "青年";
else if(mAge <60)
mPhase = "中年";
else
mPhase = "老年";
}测试函数中的一个测试用例:
CaseBegin();{
int years = 1;
pObj->mAge = 8;
pObj->Grow(years);
ASSERT( pObj->mAge == 9 );
ASSERT( pObj->mPhase == "儿童" );
}CaseEnd();
在输入数据中对被测试类的成员变量mAge进行赋值,在预期输出中断言成员变量的值。现在可以看到老纳所推荐的格式的好处了吧,这种格式可以适应很复杂的测试。在输入数据部分还可以调用其他成员函数,例如:执行被测试函数前可能需要读取文件中的数据保存到成员变量,或需要连接数据库,老纳把这些操作称为初始化操作。例如,上例中 ASSERT( ...)之前可以加pObj->OpenFile();。为了访问私有成员,可以将测试类定义为产品类的友元类。例如,定义一个宏:
#define UNIT_TEST(cls) friend class cls##Tester;
然后在产品类声明中加一行代码:UNIT_TEST(ClassName)。下面谈谈测试用例设计。前面已经说了,测试用例的核心是输入数据。预期输出是依据输入数据和程序功能来确定的,也就是说,对于某一程序,输入数据确定了,预期输出也就可以确定了,至于生成/销毁被测试对象和运行测试的语句,是所有测试用例都大同小异的,因此,我们讨论测试用例时,只讨论输入数据。
前面说过,输入数据包括四类:参数、成员变量、全局变量、IO媒体,这四类数据中,只要所测试的程序需要执行读操作的,就要设定其初始值,其中,前两类比较常用,后两类较少用。显然,把输入数据的所有可能取值都进行测试,是不可能也是无意义的,我们应该用一定的规则选择有代表性的数据作为输入数据,主要有三种:正常输入,边界输入,非法输入,每种输入还可以分类,也就是平常说的等价类法,每类取一个数据作为输入数据,如果测试通过,可以肯定同类的其他输入也是可以通过的。下面举例说明:
正常输入
例如字符串的Trim函数,功能是将字符串前后的空格去除,那么正常的输入可以有四类:前面有空格;后面有空格;前后均有空格;前后均无空格。边界输入
上例中空字符串可以看作是边界输入。
再如一个表示年龄的参数,它的有效范围是0-100,那么边界输入有两个:0和100。非法输入
非法输入是正常取值范围以外的数据,或使代码不能完成正常功能的输入,如上例中表示年龄的参数,小于0或大于100都是非法输入,再如一个进行文件操作的函数,非法输入有这么几类:文件不存在;目录不存在;文件正在被其他程序打开;权限错误。如果函数使用了外部数据,则正常输入是肯定会有的,而边界输入和非法输入不是所有函数都有。一般情况下,即使没有设计文档,考虑以上三种输入也可以找出函数的基本功能点。实际上,单元测试与代码编写是“一体两面”的关系,编码时对上述三种输入都是必须考虑的,否则代码的健壮性就会成问题。
白盒覆盖
上面所说的测试数据都是针对程序的功能来设计的,就是所谓的黑盒测试。单元测试还需要从另一个角度来设计测试数据,即针对程序的逻辑结构来设计测试用例,就是所谓的白盒测试。在老纳看来,如果黑盒测试是足够充分的,那么白盒测试就没有必要,可惜“足够充分”只是一种理想状态,例如:真的是所有功能点都测试了吗?程序的功能点是人为的定义,常常是不全面的;各个输入数据之间,有些组合可能会产生问题,怎样保证这些组合都经过了测试?难于衡量测试的完整性是黑盒测试的主要缺陷,而白盒测试恰恰具有易于衡量测试完整性的优点,两者之间具有极好的互补性,例如:完成功能测试后统计语句覆盖率,如果语句覆盖未完成,很可能是未覆盖的语句所对应的功能点未测试。白盒测试针对程序的逻辑结构设计测试用例,用逻辑覆盖率来衡量测试的完整性。逻辑单位主要有:语句、分支、条件、条件值、条件值组合,路径。语句覆盖就是覆盖所有的语句,其他类推。另外还有一种判定条件覆盖,其实是分支覆盖与条件覆盖的组合,在此不作讨论。跟条件有关的覆盖就有三种,解释一下:条件覆盖是指覆盖所有的条件表达式,即所有的条件表达式都至少计算一次,不考虑计算结果;条件值覆盖是指覆盖条件的所有可能取值,即每个条件的取真值和取假值都要至少计算一次;条件值组合覆盖是指覆盖所有条件取值的所有可能组合。老纳做过一些粗浅的研究,发现与条件直接有关的错误主要是逻辑操作符错误,例如:||写成&&,漏了写!什么的,采用分支覆盖与条件覆盖的组合,基本上可以发现这些错误,另一方面,条件值覆盖与条件值组合覆盖往往需要大量的测试用例,因此,在老纳看来,条件值覆盖和条件值组合覆盖的效费比偏低。老纳认为效费比较高且完整性也足够的测试要求是这样的:完成功能测试,完成语句覆盖、条件覆盖、分支覆盖、路径覆盖。做过单元测试的朋友恐怕会对老纳提出的测试要求给予一个字的评价:晕!或者两个字的评价:狂晕!因为这似乎是不可能的要求,要达到这种测试完整性,其测试成本是不可想象的,不过,出家人不打逛语,老纳之所以提出这种测试要求,是因为利用一些工具,可以在较低的成本下达到这种测试要求,后面将会作进一步介绍。
关于白盒测试用例的设计,程序测试领域的书籍一般都有讲述,普通方法是画出程序的逻辑结构图如程序流程图或控制流图,根据逻辑结构图设计测试用例,这些是纯粹的白盒测试,不是老纳想推荐的方式。老纳所推荐的方法是:先完成黑盒测试,然后统计白盒覆盖率,针对未覆盖的逻辑单位设计测试用例覆盖它,例如,先检查是否有语句未覆盖,有的话设计测试用例覆盖它,然后用同样方法完成条件覆盖、分支覆盖和路径覆盖,这样的话,既检验了黑盒测试的完整性,又避免了重复的工作,用较少的时间成本达到非常高的测试完整性。不过,这些工作可不是手工能完成的,必须借助于工具,后面会介绍可以完成这些工作的测试工具。
单元测试工具
现在开始介绍单元测试工具,老纳只介绍三种,都是用于C++语言的。
首先是CppUnit,这是C++单元测试工具的鼻祖,免费的开源的单元测试框架。由于已有一众高人写了不少关于CppUnit的很好的文章,老纳就不现丑了,想了解CppUnit的朋友,建议读一下Cpluser 所作的《CppUnit测试框架入门》,网址是:http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx。该文也提供了CppUnit的下载地址。然后介绍C++Test,这是Parasoft公司的产品。[C++Test是一个功能强大的自动化C/C++单元级测试工具,可以自动测试任何C/C++函数、类,自动生成测试用例、测试驱动函数或桩函数,在自动化的环境下极其容易快速的将单元级的测试覆盖率达到100%]。[]内的文字引自http://www.superst.com.cn/softwares_testing_c_cpptest.htm,这是华唐公司的网页。老纳想写些介绍C++Test的文字,但发现无法超越华唐公司的网页上的介绍,所以也就省点事了,想了解C++Test的朋友,建议访问该公司的网站。华唐公司代理C++Test,想要购买或索取报价、试用版都可以找他们。
最后介绍Visual Unit,简称VU,这是国产的单元测试工具,据说申请了多项专利,拥有一批创新的技术,不过老纳只关心是不是有用和好用。[自动生成测试代码 快速建立功能测试用例 程序行为一目了然 极高的测试完整性 高效完成白盒覆盖 快速排错 高效调试 详尽的测试报告]。[]内的文字是VU开发商的网页上摘录的,网址是:http://www.unitware.cn。前面所述测试要求:完成功能测试,完成语句覆盖、条件覆盖、分支覆盖、路径覆盖,用VU可以轻松实现,还有一点值得一提:使用VU还能提高编码的效率,总体来说,在完成单元测试的同时,编码调试的时间还能大幅度缩短。算了,不想再讲了,老纳显摆理论、介绍经验还是有兴趣的,因为可以满足老纳好为人师的虚荣心,但介绍工具就觉得索然无味了,毕竟工具好不好用,合不合用,要试过才知道,还是自己去开发商的网站看吧,可以下载演示版,还有演示课件。
-
测试工程师12最(zz)
2007-12-04
测试工程师12最
测试工程师最开心的事:发现了一个很严重的bug,特别是那种隐藏很深,逻辑性的错误.第一次发现这种问题的时候,听到上司和开发人员的表扬时,高兴的就想扭pp.不过现在慢慢矜持些了,呵呵.
测试工程师最提心吊胆的事:版本release出去后,客户发现了很多或很严重的bug.经过紧张的系统测试之后,好不容易可以轻松一下了,却又陷入了每天担心正在做验收或使用的客户一封邮件或一个电话说产品有问题.碰到好些的老板还会比较乐观的看这样的问题,最惨的就是有些人一顿臭骂,之前的辛苦,加班全部都给抹杀了.
测试工程师最憎恨听到的话:"为什么这个bug没有在测试的发现呢?"这句话经常是客户发现bug后,老板对测试人员的质问.当然这里排除那种很明显的错误.其实谁都知道bug是不可能全部发现的,这句话其实也是客户对大头,大头对小兵一级一级问下来的.除了希望测试人员警惕之外,还有更多的是一种"踢猫"的行为.对于这句话,第一次听到这句话的反映是"我们怎么可能发现所有的bug呢",后来变成"制造bug的人不是我们,是开发",到现在的"让我查查我的日志,问问开发这个bug的原因,为什么我们会没有找到,下次我们会怎样"的回复.
测试工程师最郁闷的事:"刚才那个版本打包打错了,你们要重测".新版本来了,马上投入紧张的测试,希望能够多找些bug.没想到辛苦了可能大半天,开发人员说打包打错了,你重测吧.这种情况虽然可以通过规范流程之类的办法控制发生的机率,但人总会犯错,多少而已.碰到这样,你除了提醒开发部门下次注意,你除了重测没有太多办法.
测试工程师最不想面对的事:在测试晚期或最新的版本里发现了以前一直存在的问题,特别是当问题很严重时决定到底报不报bug.报吗,开发人员肯定会问以前有没有这个问题,不报吗,客户发现更惨.毕竟客户或老板的责备比开发部门或主管的责备轻许多,最后还是会报到bug库里的.
测试工程师最不想做的事:申请版本推迟发布.由于在版本发现了太多的问题,觉得产品不能达到发布的标准,建议公司推迟发布产品.这时虽然大家都知道产品有问题,尽管你自己也不希望这样,但谁都觉得你是一个制造麻烦的人,毕竟市场的压力很大呀.
测试工程师最丢人的事:辛苦的发现了一个bug,居然是该配的参数没有配等一些自己的失误造成的.有些该注意的地方居然测试时忘了,找出的问题给开发人员一顿臭扁,无比丢人啦.
测试工程师最怕的事:一天,甚至几天都没有发现一个bug!经过一段时间的bug高峰期后,有段时间会发现bug数量的减少,最可怕的就是一天都没有发现一个bug.有时会难过的吃饭都没心情.搞得开发朋友说了一句最让人吐血的话:"要不要我在代码里放几个bug给你呀,hoho"
测试工程师最伤心的事:每年的调薪,发bonus或发股票时,测试工程师总比开发工程师少.一同事在调薪的第二天就申请转开发,说测试太没前途了.
测试工程师最有力的保护方法:把你认为是bug的问题都提交到一个正式的,可以追踪的地方(一般来说是bug库).有时总会碰到一些很小的或是很难判断的问题,犹豫一定是否要报,特别是一些UI的问题.有时问开发人员,他们可能会轻描淡写的回复你导致你没有report它.但多年的经验一定要报,了解bug流程走向的人都知道,后面还有人verify,还有开发经理判断,如果不是bug,自然他们会回复,会写明原因.说白了,出了问题也不是你的事情.当然一开始经验不足时会收到一些白眼球,但慢慢经验多了,对系统熟悉了,自然这种情况会少些.人也可以从一些问题中发现自己的弱点.但如果不报,那天客户提出来,你除了懊悔还要面对指责,严重的炒鱿鱼.
测试工程师最任重道远的事:测试驱动开发.碰到这种开发模式的项目,既是测试扬眉吐气的机会,也是可能会陷你于深渊的恶潭.你就必须打起十二分的精神.等于你在引导开发,有什么问题一定要提出来,否则你就等着被盲目的牵着鼻子走了.
测试工程师最期待的事:测试能够越来越受重视,测试工程师的考核越来越合理 -
软件测试概述(zz)
2007-11-30
软件测试概述
2.1 软件测试的概念
软件测试其实应该是伴随着软件生产而产生,有了软件生产就必然有软件测试,但直到1957年,软件测试才和软件调试区分开来,软件测试的概念也有很多版本。测试目的演变如下表:
证明 检测 预防
表明软件能工作 发现错误 管理质量
20世纪60年代 20世纪70年代 20世纪90年代
到上世纪80年代,软件质量“号角”吹响之后,软件测试的概念才逐步的稳定下来。1983年IEEE提出了软件工程标准术语定义如下:
“使用人工或自动化工具运行和测试某个系统的过程,目的在于验证它是否满足规定的需求或是弄清预期结果与实际结果之间的差别。”这个定义明确提出了软件测试以检验是否满足需求为目标。
假如给大家一个纸杯子,如何测试呢?大家可能会有很多种答案,比如:倒满水杯,测试一下水杯的容量,这个应该是最先想到的。可能会想到测试杯子的质量,体积,规格尺寸等等。甚至还会想到测试杯子的耐热性,测试杯子的耐腐蚀性等。所有这些测试方案最终的目的就是为了验证纸杯子是否满足了预期设计需求。当然纸杯的最初需求可能是很简单的,就是可以盛水,供人饮用。但质量上乘的纸杯子一定要最大程度的满足用户的需求。而测试过程就是验证这些直接需求和衍生需求是否得到实现,是否达到了预期的设计目标。再发散一下思维还可以有很多想法,比如测试水杯装热水后的传热性,使用者端起水杯是不是可以忍受住?或是采用其他形式让人端起水杯?测试纸杯是不是可以燃烧?燃烧后是不是会产生有毒的烟?测试水杯制作材料是不是有毒?装其他饮料会不会产生有毒物质?大批量纸杯处理是不是环保的?测试一下水杯的容量等物理规格是否合理?测试一下小孩子拿水杯是不是很容易?等等。可能大家还会想到很多测试方案,其中肯定不乏奇思妙想。当然有的方案需要采用复杂的测试设备和技巧才能完成,甚至需要专门的实验室。但对于一个要做出好产品的企业来说这些测试是必要的。总之开发人员通过设计来实现预期的需求目标,测试人员通过测试来验证预期的需求是否满足。
2.2 软件测试技术的广度和深度
一个纸杯有不计其数的测试方案,而对于复杂软件产品,特别是大型软件项目的测试就会涉及到很多技术环节,下面谈一下测试技术的广度和深度。
测试技术的广度体现在软件产品的种类繁多和业务领域的复杂性上。针对与不同的软件产品需要应用不同的软件测试技术。软件测试技术本身也是在不断发展,尤其是近几年。各种测试技术发展迅猛,这就使得软件测试知识体系越来越庞大。大体上软件测试可以分为以下类型和维度:
1) 按生命周期分:单元测试,集成测试,系统测试,验收测试。
单元测试:又称为模块测试,是针对软件结构中独立的基本单元进行测试。是对单元设计文档的验证过程,通常在编码阶段进行的。
集成测试:又称为组装测试,可以根据集成策略对软件模块进行组装后测试。
系统测试:是将已经集成好的软件系统,作为整个基于计算机系统的一个元素,与计算机硬件,外设,某些支持软件,数据和人员等其他系统元素结合在一起,在实际运行环境下,对计算机系统的一系列测试。系统测试根据测试类型又分为功能,性能,容量,GUI测试等测试技术。
2) 按测试方法:分为黑盒测试,白盒测试,灰盒测试。黑盒测试包括功能测试用例设计技术;白盒测试包括词法语法分析,静态错误分析,测试驱动技术,程序分析技术。灰盒是介于黑盒与白盒之间的测试方法。
3) 测试过程模型开发和测试项目管理。
4) 按执行测试方式:人工手动测试,自动化测试技术。其中自动化测试技术又包含了功能自动执行和回归自动化测试,性能模拟自动化测试,数据自动生成,测试过程管理自动化技术,嵌入式自动化。另外还包括整套的测试过程管理解决方案等。
5) 专用系统和专项测试技术。
测试技术不光体现还广度上还体现在深度上。涵盖了计算机网络,操作系统,处理机,计算机体系结构,软件架构,编码,软件工程等计算机科学领域的多门学科。所以作为测试工程师,这些领域的知识掌握的越深,测试的效果也就越好。
2.3 软件测试工作的特点和重要性
记得<<人件>>书中提过黑衣团队这么一个概念,大意是一公司为了提高软件产品质量,将那些非常有才能的测试工程师组成一组,并给他们特权,让他们在软件产品上市之前进行最终的测试。这个团体逐渐形成自已的个性,也发展了一种渴望并期待发现产品缺陷的哲学。为了更加有个性,他们开始都穿上黑色的衣服,程序一旦有BUG他们就可怕地笑起来。他们的测试根本不是在支持开发人员,而是乐于将程序与程序员放到一种不是测试而是折磨的工序下面。他们还经常聚在一起研究出十分可怕的测试策略,他们一些变态的想法与测试方法让程序员望而生畏欲哭无泪,程序员越觉得糟糕,他们就越觉得过瘾与高兴。虽然这个故事比较夸张但也从另外的角度反映了测试工程师在一定程度上是与开发人员处于对立面,程序员类似于建设者,而测试员更像是破坏者。
其实事实并不是这样的。测试组和开发组同属一个项目组,有着共同的目标,就是生产出高质量的软件产品,测试组发现缺陷增加了开发组的前期工作量,但却能减少项目后期的维护工作量和成本。而如果用户拿到了满是bug的软件产品,损失的不光是后期的维护成本,更重要的是损失了公司的质量信誉。所以测试组和开发组的关系应该是密切配合,通力合作的关系。实践也证明,测试人员与开发人员沟通越充分,测试的效果越好。 -
测试生活
2007-11-23
发现每天忙的很没有头绪,该睡了才发现很多事情还没做完,可见计划是很重要的。
学了测试,每个阶段也是要有计划的,我们每天的生活学习也该是有计划的才能事半功倍。一直想把老师讲的重点内容整理出来,熟记一下,比如一些问答题,还有一些英文缩写的意思;每到第二天上课老师提问了才从头来翻翻看,虽然具体内容回答不上来,却记得老师是讲过的,有印象的,所以痛下决心要整理出来。每每回到家又被其它琐事耽误了,所以总是于心不忍,今天说什么也要总结整理出来,温故知新。前一天就要计划好第二天要做的比较重要的事情,做完了再做其它事情。
给我们上专业课的现在只有两位老师:徐老师,商老师。两位老师讲课风格不同:商老师思路清晰,普通话很标准,总是启发我们多问几个为什么;徐老师对于测试比较专业,人也很幽默,上课特点就是发散性思维方式。
老师的作用就是一个领路人,告诉我们到底要学什么(what),要怎么做(how),具体做的过程就因人而异了,所以最后的结果(result)也就千差万别。说到计划想起来上课学的一部分内容(PDCA),不论做什么事情先要有计划(plan),然后去做(do),做完之后检查做的结果(check),好的地方继续坚持,不好的地方改进,然后再去做(act)。总觉得do和act是有区别的,查了字典,明白了一点。
act
[Akt]
n.
幕, 法案, 法令, 动作, 举动, 节目, (戏剧的)幕
vi.
行动, 产生...的效果, 担当, 表演, 假装, 表现, 见效
vt.
扮演, 装作
do
[dEu]
v.
做, 实行, 尽力, 给予, 可用, 制作, 算出, 解答
[域] Dominican Republic , 多米尼加共和国
(以上叙述纯属个人观点.)一切都要落实在行动中=*^*= -
(转载)编写Bug report的原则
2007-11-19
编写Bug report的原则Bug report是测试中最重要的一部分,也是测试人员价值的终极体现,一个有效的Bug report,在编写的时候需要遵循以下原则:(1) Bug 可重现,尽可能找到重现规律。测试人员在编写Bug report之前必须在检查问题是否可重现,问题重现才可以让开发更有效地查找到原因并解决问题,对于比较复杂的问题,最好能够将Bug现场重现给开发人 员,以方便问题追踪和原因定位。如果错误不可再重现,仍然应该写下来,但是必须说明问题的偶然性。(2) Bug描述简明准确,对于问题的描述,应该尽可能简明、准确。隐含的或模糊的说明和那些由于对没有任何关系的细节或者那些在重现错误过程中不需要的步骤,不要出现在Bug report中。(3) 一 个Bug report只描述一个Bug,如果将几个问题都写在一个Bug report中,开发人员很难有效发现自己的问题并解决,从而导致有些优先级别高的Bug没有得到及时的解决。因此在写Bug report的时候,将Bug按照不同的优先级别将不同的问题指定给相应的开发人员。(4) Bug的唯一性,在提交Bug report之前,要先确认这个Bug是否已经被其它人发现并报告。衡量优秀的Bug report的质量指标:(1) 对管理层来说,是清晰明了的,特别是在主题概要这一级;(2) 对于开发人员来说,是有用的,主要是提供能够让开发人员高效地调试问题的相关信息,使其可以很快的将Bug从“Opened”状态转变成“Closed”状态,提高测试和开发的工作效率;(3) 对于后期的维护,能够有效从Bug信息查询出问题的描述和解决的方法。二、 如何编写Bug reportBug report作为测试和开发之间沟通的桥梁,测试人员在报Bug的时候,有效的Bug描述,会更加容易帮助开发解决问题。一般来说,作为一个优秀的Bug report,应该包括以下内容:1、 标题:简明扼要地对Bug进行一个概述,让人看标题就知道大概出现了什么问题。比如:“smfilter模块在压力测试时出现内存泄露。”Bug2、 的属性,Bug的属性应该包括:Bug(1) 产品名称:测试产品的名称。(2) 产品子系统:测试产品的子系统,如果产品比较小,该项可以忽略。(3) 产品模块:测试产品发现问题的模块的名称。(4) 测试版本:当前的测试版本。(5) 测试平台:Bug的产生跟平台有关,有些在suse下产生的Bug,在soralis下则正常,因为在报Bug的时候需要将当前测试的服务器的版本。(6) 测试阶段:模块测试、内部集成测试、外部集成测试。(7) 问题级别:紧急、严重、一般、轻微(8) 优先级别:高、较高、一般、低(9) 问题来源:测试、工程故障、升级、其他(10) 问题类型:功能问题、版本问题、遗留问题、新需求、低级错误、改进建议、移植修改、割接问题、配置错误、编译问题、性能问题、设计问题、兼容问题、新功能增强、偶发性出错这些属性在Bug跟踪管理系统中应该有默认值,在测试人员报bug的时候选择对应的属性值。3、 负责人:Bug(1) 开发人员:测试产品模块的开发人员(2) 测试人员:发现Bug的测试人员(3) 抄送:该Bug需要抄送给相关的开发人员或测试人员。一般来说,一个Bug除了发送给改Bug的开发负责人和测试负责人外,还需要抄送给项目经理、测试经理、该产品开发小组其他人员,该产品测试小组其他人员这些属性在Bug跟踪管理系统中也应该有默认值,在测试人员报bug的时候选择对应的负责人。4、 Bug的详细描述:这是Bug最重要的一部分,对Bug描述清晰准确,不仅有助于开发人员迅速定位解决问题,还对以后的维护工作有很大的帮助。一些比较简单的Bug,可以使用一两句话把问题准确描述,而对于一些比较严重或负责的Bug或者是新的需求,则应该详细说明。5、 附件:对于一些特殊的问题或者不能用语言很好地描述的问题,可以增加界面图形说明或参考资料或详细日志等附件。6、 其它属性:(1) BugID:Bug的唯一标志(2) 建档时间(3) 建档人(4) Bug回复时间(5) Bug关闭时间一般来说,在报bug之后,这些属性一般由bug跟踪管理系统自动生成。三、 如何划分bug的严重级别Bug的严重级别指的是软件缺陷对软件质量的破坏程度,也就是该软件缺陷的存在将对软件的功能和性能产生怎样的影响。在软件测试中,软件缺陷的严重级别应该从软件最终用户的立场来做判断,考虑缺陷对用户使用造成的后果的严重性。1、 紧急(1) Bug足以造成系统崩溃,造成文件不可靠或潜在的数据丢失(2) Bug造成非正常地返回操作系统(系统崩溃或显示系统错误信息)(3) Bug造成程序越或要求Reboot系统(4) 造成缺乏关键的程序功能并无法逾越(5) 由于设计问题,使系统存在严重隐患2、 严重(1) Bug可能不会削弱系统,但将造成严重问题(如严重的格式化错误等)(2) 功能缺乏给用户带来极大不方便(3) Bug可以绕过,但将会很不方便或很难实现(4) 存在不明确或不完整的错误信息提示,极大地影响产品使用(5) 由于Bug的存在使产品其它部分不能测试(6) 由于计算方法问题,使数据错误(7) 由于设计原因,造成前后不一致,但问题可恢复3、 一般(1) 这个Bug在将来是严重的,但比主要问题要轻。(2) 可以有一个比较简单的绕过Bug的解决方案(3) 存在不明确或不完整的错误信息提示,但对产品使用影响较小(4) 容错性方面存在不足(5) 由于精度问题造成数据不一致4、 轻微(1) 不可能被用户发现的Bug(2) 某个控件没有对齐,某个标点符号丢失等低级错误(3) 尚无法满足的新需求四、 如何划分bug的优先级别Bug的优先级是指解决软件缺陷的先后顺序,即哪些缺陷需要优先解决,哪些缺陷可以稍后解决。确定软件缺陷优先级,更多的是站在软件开发工程师的角度考虑问题,同时需要考虑问题修改的成本与时间。1、 高这类Bug对产品影响非常大,造成产品不能移交。2、 较高这类Bug对产品影响比较大,如果产品带着Bug发布给用户将会产生麻烦。3、 一般这类Bug对产品影响一般,如果Bug被fix,产品会更好;或者是这类Bug对产品影响一般,如果fix bug 不影响产品的交付期,一定要fix。4、 低这类Bug对影响较小,只有在其它所有Bug已经被fix后,再fix这类Bug一般来说,严重级别高的bug具有较高的优先处理级别,但是严重级别和优先级并不总是一一对应。有时候严重级别高的Bug优先级不一定高,而一些严重级别低的Bug却需要及时处理,具有较高的优先级。例如,如果某个严重的bug只在非常极端的条件下产生而通过配置或人为注意可以避免,则可以稍后解决。另一方面,如果软件缺陷的严重性很低,例如,提示错误,但是提示信息很容易造成别人的误解,则必须尽快修改。五、如何进行Bug描述1、 简明扼要的bug描述一些比较简单的Bug,可以使用一两句话把问题准确描述,比如:“在管理网页新增用户,当新增的用户登录名名称很长(例如登录名长度为输入框允许的最大长度),按‘新增’按纽新增后系统提示已经有该用户存在,而事实上该用户并不存在,建议对超长的用户名进行处理。”2、 严重的程序缺陷bug描述比较严重或复杂的Bug,应该在bug报告中进行详细的说明。对于产品的程序缺陷,也就是测试的时候发现的问题,此时作为Bug的详细描述,应该包括(根据实际情况选择):(1) 测试要点:测试用例的测试要点(2) 测试配置:如果有特殊的配置,需要在Bug描述中说明(3) 测试环境:发现Bug的测试环境(4) 测试步骤:详细描述发现Bug的操作步骤和流程(5) 实际结果:按照测试步骤测试后实际出来的操作结果(6) 预期结果:按照测试步骤测试后原本预期的操作结果(7) 测试结论:通过对比实际结果和预期结果,“诊断”程序出错原因(8) 归纳引申:在测试人员发现了一个已隔离的,可重现的问题后,应该对问题进行归纳。同一个问题是否出现在其他的模块或其他的地方?同一个故障是否有更加严重的问题?举例:(1) 试点:检查点对点互通(简称P2P)网关系统短信过滤模块smfilter在大压力业务的时候是否有内存泄露或CPU暴涨。、(2) 测试配置:将系统所有模块的配置都调整到最优化配置,数据库中导入100万个手机过滤黑名单和50万个过滤关键词。(3) 测试环境:smfilter安装在10.3.3.92,其余业务模块安装在10.3.3.91,测试工具安装在10.3.3.96,均为suse9操作系统;数据库安装在10.3.3.96,为oracle9204版本。(4) 测试步骤:A、清理测试环境,确保测试环境干净。B、 使用top查看系统各模块资源在正常范围之内:smfilter的内存使用5%(10M),CPU使用率为6%。C、 使用手机模拟测试工具向P2P网关发送1000万条MO短信,同时使用网关模拟工具向P2P网关系统前转1000万条MT短信;其中各有200万条MO/MT短信含有是黑名单号码,200万条MO/MT短信内容含有关键词。D、观察smilter的内存使用率和CPU使用率是否上涨,发现CPU使用率在正常范围之内缓慢上升,但内存使用率则不断狂涨。(4)实际结果:短信发送高峰期,CPU使用率为56%,短信发送完毕CPU使用率恢复到8%;而内存使用则从原来的10M一直上涨到1.6G,且短信发送完毕也没有下降。(5)预期结果:在压力测试过程中,CPU和内存应该在正常范围内缓慢上升,短信发送完毕应该恢复到测试开始时的使用率。(6)测试结论:smfilter在大压力的情况下出现内存泄露的情况,请及时查找原因并解决。3、 新需求Bug描述在 后期维护中,对于新的需求,也作为Bug记录进Bug跟踪管理系统。在作为一个Bug增加进系统之前,应该由项目经理、开发人员和测试人员对新需求进行了 评估和讨论,最终决定了解决方案。在这个过程中,测试人员充当着一个非常重要的角色,需要对需求非常明确,而且应该从系统的高度和设计的高度全面考虑需求 的实现。此时作为Bug的描述,应该包括(根据实际情况选择):(1) 求说明:详细的需求要点。(2) 配置说明:是否需要增加或修改配置,如果需要则详细说明应该增加或修改的配置。(3) 数据库结构:是否需要新增或修改数据库表结构,如果需要则详细说明新的数据库结构。(4) 实现方法:实现新需求采取的策略或技术,这部分通常是在讨论的时候由项目经理和开发人员决定的,当然,测试人员应该审核采取这样的技术是否真的合理,这就要求测试人员有比较强的技术背景了。(5) 业 务流程:新需求在处理过程的业务流程,对于业务流程该如何处理,测试人员事先一定要先确认,并在Bug描述中进行详细的描述,使开发实现新需求以及测试人 员验证新需求提供有效的依据。根据经验,有些时候,开发也不会很全面考虑整个系统的业务流程,经常会出现新增功能增加之后新问题又出来的情况,如果测试人 员可以在描述中清楚说明整个业务的处理流程,就可以大大减轻开发工作量和测试工作量了。当然,前提是测试人员自己一定要对业务流程有一个非常清晰的概念, 对于一些不能确认的流程,在事先可以征求项目经理的意见或与开发讨论确认。(6) 异常处理:在新增一个功能的时候,需要考虑这个功能可能会出现什么异常,这些异常出现的时候系统该如何处理。举例:(1) 需求点:为了提高高峰期点对点互通网关短信状态报告处理效率,增加状态报告缓存处理功能(2) 新增配置项:在ptop模块的配置文件ptop.ini中增加以下配置:[SERVER]CacheStatus = 0 //是否需要启用短信状态报告缓存处理功能,0表示不启用,1表示启用,默认配置为0。CacheStatusDir = SMS01 //缓存状态报告的目录,每个smsPtop必须唯一FetchInterval= 10 //状态报告缓存进行处理的时间间隔,单位:豪秒(3) 业务流程:A、若启用了状态报告缓存功能,接收到短信状态报告后做简单判断并给对方回响应;B、将状态报告缓存到CacheStatus目录;C、新起一条线程处理状态报告,FetchInterval时间间隔扫描一次状态报告目录,并处理状态报告;D、若不启用状态报告缓存处理功能,则处理流程与原流程相同。(4) 异常的处理:A、接收到状态报告后如果存放失败,则马上处理状态报告;B、若出现线程池满,则先sleep一秒,然后再处理状态报告;C、 模块重新启动后,若有状态报告积压,优先将积压数据取出来处理。Bug 报告的确是需要花费一定的时间,但是也给予效果显著的回报,使产品获得更好的质量。一方面,一个优秀的Bug report可以轻易地打开开发人员和测试人员之间的积极关系,在时间上前期花费在编写优秀Bug report上的时间和后期由于返工差的Bug report花费的时间相抵消;另一方面,也可以有效改进测试小组和高层、平行管理层之间的沟通,增强测试小组的信任度。六、如何进行Bug回复描述开 发人员修改问题之后,将Bug回复给对应的测试负责人。很多开发人员在回复的时候只是简单地用“已解决”或“fixed”这样的语句,对于简单浅显的问题 来说,这样已经足够。而对于复杂或重要的问题,在回复的时候应该详细说明测试的解决方法,一般来说,Bug回复应该包括:(1) 问题原因:导致Bug产生的根源(2) 问题结果:问题是否已经被解决说明,如“问题解决”、“下一版本解决”、“无法解决”等(3) 配置说明:在fixed Bug的时候是否需要增加或修改配置项,如果需要则详细说明配置。(4) 数据库结构:解决此问题是否需要改动数据库表,如果需要则需要详细说明。(5) 目标文件:解决此Bug的代码文件以及涉及到的相关代码文件文件(6) 解决方法:解决此Bug的方法(7) 改动代码:解决此Bug的时候有哪些代码被改动或增加,并将新的核心代码在回复中粘贴出来。(8) 业务流程:解决此Bug或新增功能的业务流程是否变更,如果有则详细说明新的业务流程,对于特殊的异常的处理也应该进行详细的说明。开发人员回复Bug之后,测试人员会进行验证,如果问题还没解决或修改后引进了新的问题,则将这个Bug重新回复给开发人员,并且在回复中进行详细的问题描述。七、如何进行Bug 关闭描述开 发回复Bug之后,测试负责人验证该Bug,如果问题得到解决则关闭(否则回复给开发负责人,让其继续追踪)。关闭一个Bug时,大多数的测试人员都是采 用“问题解决”或“OK”这样的语句回复,作为一个简单的问题这样回复已经足够。但是对于一些比较复杂的问题或需求,可能经过了多次的修改,修改的最终结 果已经跟Bug描述有相当部分的出入了,因此在关闭Bug的时候,应该对Bug描述的内容进行一个总结:(1) 问题结果:问题是否已经被解决说明,如“问题解决”、“下一版本解决”、“无法解决”等;(2) 配置说明:软件问题解决之后最终需要新增或修改的配置文件和配置项目;(3) 数据库结构:软件问题解决之后,软件升级涉及到修改的数据库表结构有哪些以及需要执行哪些脚本;业务流程:Bug fixed之后的业务流程,如果是新需求,还要进行新功能点说明。
-
学习体会
2007-11-19
学习了三周了,感觉到自己很多不足的地方,编程能力尤其弱...
一个学生信息管理系统,老师给了我们程序,让大家分析。应该是把学生信息存储在链表里,我却认为是数组,后面画流程图也是费尽,愣是画不明白,以后千万不能让人知道我是计算机专业的学生了-^-
上周的课有节是讲验收测试的,最后老师让把客户验收过程补全,把里面的角色,职责,入口准则,出口准则写出来。我把文档前前后后看了很多遍却不知道怎么下手*^*
这段时间学的都是很抽象的理论,对所有概念都是知道定义,并没有直观的认识,所以很多东西只能死记。武侠小说里面的练功人都是先背过秘笈,熟记在胸才慢慢琢磨练成高手的,当你不明白里面真正的含义的时候只能这么做,Z以前就这么对我讲过,只恨自己记性太不好~~
课前预习课后复习很重要,第一阶段快要学完了,应该把第一册的书自己预习完然后总结复习。
永远不要给自己找借口,不要抱怨。
If you don’t know where you are,a map won’t help。
-
香山行
2007-11-14
周一爬了香山,现在才缓过劲来~~
也许是早上下了些小雨的缘故,也许是有些学友住的比较远,那天早上大家没有全来,似乎有点小遗憾,只怪天公不作美,不过大家玩得还是很开心,这就足够了=*^*=
本以为周一会堵车,所以估计要一个小时才能到香山脚下,没想到那公交半个小时飞到了,得出一点小结论:在你不熟悉的环境下,很难对事情做出合理判断。等了一会就有不少人来了,十点半左右正式登山,一路就是拾级而上。道路两旁没有太多红色的叶子,所谓的香山红叶只是一株瘦小的植物,一人多高,绿草地上落了很多血红的叶子,很多人捡了作为留念,大多时候树上的叶子都是半黄半红的。刚下过雨,山里的空气格外清新,太阳还没出来,整个山都是笼罩在薄薄的雾气之中,想起一句诗来:只在此山中,云深不知处。远远望去很像是进了人间仙境,影影绰绰,后面有照片为证。
路上大家走走歇歇,说说笑笑,遇到好的景点停下来合影留念,不亦乐乎。最值得一提的是,还见到了明星,和袁泉擦身而过...快到山顶的时候,大家都很累了,我一抬头看到对面下山的人里有个戴着大帽子,戴墨镜的人,因为与众不同就多看了两眼,正当她经过我身边的时候发现她的嘴角很像一位明星,当我脱口而出她的名字的时候,她回头看了一下马上就继续下山了。我不是追星族,也能体会做明星的难处,大家热的冒汗,她还要戴大帽子;也许她想停下来说声你好,那样的话也许会阻塞交通。最搞笑的是,学友们郑重告诉我下次如果看到明星一定要大声喊,告诉他们一下,哈哈~~
到了山顶才发现山原来并不高,大家一块做游戏,我被罚唱了一首歌,因为没准备,所以选了一首自认为不能代表本人水平的歌,下山的路上就开始后悔。下山的路是我以前从未走过的,路上人很少,就我们的一队人最抢眼,只是下山,两边没有景点,开始以为走错了路,不过正应了那句诗:“山穷水尽疑无路,柳暗花明又一村”。以为这次登山就这样结束的时候,发现两处大的景点就在离我们不远的地方:双清别墅和香山寺遗迹。
到了山脚下四点多了,各自离开回家,坐在公交车上差点睡着了~~
昨天拿到某学友给我们拍得照片,加上这次是第三次爬香山,不过照相还是第一次,虽说照片有点模糊,不过还是要感谢他*^*
-
(转载)软件能力成熟度模型:CMM
2007-11-14
CMM 为企业的软件过程能力提供了一个阶梯式的进化框架,阶梯共有五级。第一级只是一个起点,任何准备按CMM 体系进化的企业都自然处于这个起点上,并通过它向第二级迈进。除第一级外,每一级都设定了一组目标,如果达到了这组目标,则表明达到了这个成熟级别,可以 向下一级别迈进。
从纯粹的个人行为发展到有计划有步骤的组织行为…
第一级:初始级(Initial);
第二级:可重复级(Repeatable);
第三级:已定义级(Defined);
第四级:受管理级(Managed);
第五级:优化级(Optimizing)。初始级
初始级的软件过程是未加定义的随意过程,项目的执行是随意甚至是混乱的。也许有些企业制定了一些软件工程规范,但若这些规范未能覆盖基本的关键过程要求,且执行没有政策、资源等方面的保证时,那么它仍然被视为初始级。
关注点:
工作方式处于救火状态,不断的应对突如其来的危机;
工作组:软件开发组、工程组;
提高:
需要建立项目过程管理,建立各种计划,开展QA 活动。
可重复级
根据多年的经验和教训,人们总结出软件开发的首要问题不是技术问题而是管理问题。因此,第二级的焦点集中在软件管理过程上。一个可管理的过程则是一个可重 复的过程,可重复的过程才能逐渐改进和成熟。可重复级的管理过程包括了需求管理、项目管理、质量管理、配置管理和子合同管理五个方面;其中项目管理过程又 分为计划过程和跟踪与监控过程。
通过实施这些过程,从管理角度可以看到一个按计划执行的且阶段可控的软件开发过程。
关注点:
规则化
引入需求管理、项目管理、质量管理、配置管理、子合同管理等;
引入工作组:测试组、评估组、质量保证组、配置管理组、合同组、文档支持组、培训组;
提高:
SEPG、建立软件过程库和文档库。
已定义级
在可重复级定义了管理的基本过程,而没有定义执行的步骤标准。在第三级则要求制定企业范围的工程化标准,并将这些标准集成到企业软件开发标准过程中去。所 有开发的项目需根据这个标准过程,裁剪出与项目适宜的过程,并且按照过程执行。过程的裁剪不是随意的,在使用前必须经过企业有关人员的批准。
关注点:
文档化,标准的一致的;
软件过程标准化文档化,质量可以得到控制;
工作组: SEPG、软件评估组。
提高:
对软件过程定量分析,加强质量管理。
已管理级
第四级的管理是量化的管理。所有过程需建立相应的度量方式,所有产品的质量(包括工作产品和提交给用户的最终产品)需要有明确的度量指标。这些度量应是详尽的,且可用于理解和控制软件过程和产品。量化控制将使软件开发真正成为一种工业生产活动。
关注点:
量化,可预测的;(自此,软件开发变成一种工业生产活动。)
软件过程具有精确的评测方法,量化的控制软件过程的产品和质量,可根据”意外情况”确定出错的原因;
工作组: 定量过程管理组;
提高:
防止和规避缺陷的能力,技术革新的能力,过程改进。
优化级
优化级的目标是达到一个持续改善的境界。所谓持续改善是指可以根据过程执行的反馈信息来改善下一步的执行过程,即优化执行步骤。如果企业达到了第五级,就表明该企业能够根据实际的项目性质、技术等因素,不断调整软件生产过程以求达到最佳。
关注点:
持续改善;
工作组:缺陷防范活动协调组、技术改革管理活动组、软件过程改进组;
改进:
软件过程优化。
-
软件质量(二)
2007-11-12
1.软件能力成熟度模型(CMM模型)的提出是源于评估软件承包商的能力,协助软件组织改进过程,提高过程能力
2.CMM五级过程能力及其各自特点,关键过程域
a.初始级
特点:软件过程是无序的甚至是混乱的,对过程几乎没有定义,成功取决于个人努力,管理是消防式的;
b.可重复级
特点:建立了基本的项目管理过程来跟踪费用,进度和功能特性,制定了必要的过程纪律,能重复早先类似应用项目取得的成功;
KPA(key process area 关键过程域): 需求管理,配置管理,软件质量保证
c.已定义级
特点:已将软件管理和工程两方面的过程文档化,标准化;组织中有一个专门负责软件过程的组(SEPG软件工程过程组);过程能力是标准的,一致的;
KPA:同行评审
d.已管理级:
特点:收集对软件过程和产品质量的详细度量,对软件过程和产品有定量的理解和控制;
KPA:引入软件度量
e.优化级
特点:通过对缺陷分析改进过程,从而达到对缺陷预防;新技术的引进促进改进过程
KPA:缺陷预防
3.六西格玛管理法
六西格玛模式的本质是一个全面管理概念,而不仅仅是质量提高手段;开始适用于制造业,后来用于政府机构
三:软件质量模型(六大特性,27大子特性)
1.功能性
适合性,准确性,互操作性,保密安全性,功能性的依从性;
2.可靠性
成熟性,容错性,易恢复性,可靠性的依从性;
3.易用性
易理解性,易学性,易操作性,吸引性,易用性的依从性;
4.效率
时间特性,资源利用性,效率依从性;
5.维护性
易分析性,易改变性,稳定性,易测试性,维护性的依从性;
6.可移植性
适应性,易安装性,共存性,易替换性,可移植性的依从性;




