软件测试技术实战:设计、工具及管理

978-7-115-45392-1
作者: 顾翔
译者:
编辑: 张涛

图书目录:

详情

本书通过总结作者十几年在软件测试中的实践、体会和思考,讲解了软件测试中的实战技术、方法和工具,帮助读者打开一扇通往软件测试之路的大门,寻找到解决测试问题的技术和方法,体验到测试工作中逮Bug犹如“寻宝”的乐趣。本书可供软件测试同仁借鉴。由于现在许多大学里计算机专业都开设了软件测试课程,所以本书也可作为计算机软件专业学生的参考书。

图书摘要

版权信息

书名:软件测试技术实战:设计、工具及管理

ISBN:978-7-115-45392-1

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

• 著    顾 翔

    组  编 51Testing软件测试网

    责任编辑 张 涛

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

    邮编 100164  电子邮件 315@ptpress.com.cn

    网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

    反盗版热线:(010)81055315


本书是作者总结十几年的软件测试的实践经验写成的,希望通过来自一线的实践知识和技能,帮助读者打开一扇通往软件测试之路的大门,寻找到解决测试问题的技术、技巧和方法,体验到测试工作中“逮”Bug犹如“寻宝”的乐趣。全书分为“设计”“工具”和“管理”3篇,共14章,每章的内容虽有一定的联系,但也可各自独立,读者可以根据自己的需求,按照书的内容顺序阅读,也可以根据自己的兴趣选取相关章节阅读。

本书可供软件测试初学者、测试从业人员阅读,也可作为大专院校计算机软件专业学生的参考书,还可作为培训学校的教材。


编 委:

王 威 具有多年软件开发经验和软件测试工作经验,对产品测试的方法和管理流程有深刻的认识,51Testing软件测试培训高级讲师

周 峰 国家认证系统分析员,上海软件行业协会软件质量管理与过程改进专委会副主任,51Testing软件测试培训高级讲师

王 琰 具有丰富的通信终端产品的测试以及管理工作经验,51Testing软件测试培训高级讲师

朴春龙 Mercury认证CPC,自动化测试专家,51Testing软件测试培训高级讲师

周春江 具有多年通信协议和通信终端设备的测试工作经验,51Testing软件测试培训高级讲师

商 莉 多年从事软件开发、软件测试及质量保证方面的管理工作,51Testing软件测试培训高级讲师

宋光照 具有深厚的开源软件测试实践经验,擅长嵌入式测试,51Testing软件测试培训高级讲师

刘爱飞 性能测试专家,Web测试专家,51Testing软件测试培训高级讲师

丁佳晶 多年软件测试从业经验,软件质量检验高级工程师,51Testing软件测试培训高级讲师

许爱国 国内软件测试最早一批实践者,具备丰富的测试经验,51Testing软件测试培训高级讲师

 

编辑部成员:张晓晓 严代丽


我与顾翔先生是同学,因此结缘,有幸成为《软件测试技术实战—设计、工具及管理》一书最早的读者之一。一般说来,阅读专业性、技术性强的著作是枯燥、单调和乏味的,但从作者手中拿到书稿,我却是一气呵成,几乎是没有中断读完的。

我国的软件产业发展实现了伟大的跨越,取得了辉煌的业绩。2000年,我国软件产业的规模只有590多亿元,2016年这一数字达到了惊人的4.3万亿元。这是3.8万家软件和信息技术服务企业、近600万软件从业人员努力奋斗的结果。其中也汇集了本书作者为代表的一批软件测试工程师群体的默默奉献。

在软件大发展的时代,软件的稳定、安全和可靠性尤为关键,软件测试的重要性尤为凸显。然而长期以来却鲜有系统、全面分析、研究软件测试理论技术和方法的专著问世,根植于软件测试实践而又高于实践,具有实战性、可操作性的图书更少。本书的出版发行,不可多得,正当其时。

作者1997年大学毕业后投身于软件开发、测试专业之中,20年从未间断。他不仅见证了我国软件产业波澜壮阔的发展历程,而且深深地参与其中,完成了从幼稚到成熟、从青年到中年、从最底层的软件开发和测试者到软件测试的组织管理者、教学培训导师的转变和升华。作者将20年从业的实践经验、理论思考集成于一书,形成了本书许多亮点和特色:专业性,系统、全面研究分析了软件测试的理论、方法、技术;权威性,作者虽然不是专家、名人,但我认为“因为专业而权威”;系统性,源于其本人的亲自实践、亲身经历和体验;可读性,注重以实际案例为驱动,娓娓道来,不少章节都颇为“抓人”;实用性,既可作为软件测试的教材,也可作为软件开发、测试人员的实用手册。

综上所述,我愿为本书鼓与呼,推荐给广大读者学习。

——中国信息化周报  社长  宋波


本书是由国内具有丰富测试经验的作者写作的关于软件测试实践的书,全书内容丰富,结构清晰,涵盖测试设计、测试工具与测试管理3部分内容。全书内容强调测试实践,既有测试基本知识的介绍,也有测试应用的案例分析;既可以作为测试初学者入门用书,也可以供测试职业人士参考。

——中国软件测试认证委员会(CSTQB)专家组成员 崔启亮

本书作者顾翔老师有着多年的培训经验,这本书是他多年传道、授业、解惑的总结,内容全面、精彩,可为软件测试工作人员提供专业的指导。

——中国赛宝(华东)实验室 陈锃基

从我十多年的软件测试行业从业经验来看,大部分软件测试人员缺乏对被测对象所涉及的IT技术的深入理解,例如对代码、中间件、数据库、虚拟化、云计算、大数据的理解,缺乏这些技术上的理解,表面上看起来不会对测试工作造成很大的影响,其实却会导致测试的不周全、不深入;顾翔老师知识面广,测试经验丰富,在编写本书时不仅全面透彻地讲解了软件测试本身的知识,还用通俗易懂的语言介绍了各类相关技术,测试初学者们可以全面学习并从中受益。

——广州亿能测试技术服务有限公司CEO 陈能技

本书作者经过长期的工作实践,结合国内测试现状,精心构思了测试的知识体系。书中将测试设计提升到很关键的高度,用好的测试设计指导测试工具在测试执行中的使用;用测试管理的精髓,引导测试工作的有序开展,为广大测试工程师的学习提供了必备的参考。

——科大讯飞 CTO助理 测试部经理 吴如伟

本书的特色在于将大量的技术赋予了实际的应用场景,让读者能够更加清晰地把握如何将测试技术应用于具体实践的同时,为测试团队实施相关技术方案带来信心。本书的技术内容和流程方法倾注了作者多年的从业经验,读者可以基于该书的内容为模板,迅速展开合理、规范和有序的测试工作。书中所讲述的测试技术都是作者精心选取的主流技术,体现了作者丰富的行业经验和对新技术发展方向敏锐的洞察力,初级以及资深的读者均可受益于本书的精彩内容。

——星云测试CEO 赵明

本书是一本非常全面的软件测试实践参考书。它不仅知识体系完整,作者也结合自己多年的测试实战经验,分享了大量非常有参考价值的实战案例。它不只是一本专业软件测试人员需要学习的参考书,对于做敏捷开发的每一个团队成员来说,都需要认真学习本书当中的测试基础知识、测试设计和管理的思路、实践和工具。

——Scrum中文网和Leangoo看板创始人 廖靖斌


软件测试是软件研发过程中的一个重要环节,作为一个独立的工作是在我国20世纪末和21世纪初逐渐形成的。随着软件行业的发展,至今已有一支十分庞大的专门从事软件测试工作的队伍活跃在软件企业中。

我是国内最早一批从事软件测试的工程师,先后在北京炎黄新星互联网络有限公司(公司产品:中国家庭网和800buy电子商务网站)、中兴通讯(南京)有限公司、意法半导体(中国)有限公司(公司产品:数字电视机顶盒)以及爱立信(中国)通信有限公司等单位工作过。十几年来,软件测试从无到有,我经历了整个过程,所以对软件测试有比较深入的了解和体会,也积累了一些经验。我把在工作中遇到的一些问题和案例写成数十篇文章,在51Testing等各大网站上发表,得到广大软件开发和测试人员的认可和支持,遵照一些朋友的建议,我把网上的这些文章重新整理修改,并增加了一些新的内容,集结成一本书。在这本书中,我主要以案例为驱动,介绍软件测试工作中一些常用的方法、思路、遇到的问题以及解决这些问题的方法。

1997年,我毕业于北京工业大学计算机学院软件工程专业,在学校里,软件测试仅仅作为《软件工程》的一个章节进行介绍。毕业后,我进入一家互联网公司从事网站的开发工作。当时软件测试在许多单位都不是一个独立的部门,软件测试一般都由开发人员自己来完成。由于没有专职的软件测试人员,所以软件的安全性、稳定性、可靠性等都很难得到保证。实际工作中遇到过不少案例,下面几个例子就可以得到证实。

2000年我所在的公司与CCTV“开心辞典”栏目组合作开发网上答题的项目,这是一个智力娱乐性节目,我编写了前端的答题代码,考虑到可能有人用计算机程序来答题,如编写一个死循环,一直选择B(或A、C、D),这可以使答题的速度很快,命中率也非常高,为此,我选用JavaScript过滤了使用死循环的答题者。可是,到了“开心辞典”正式使用这个软件时,发现仍然有人使用死循环来答题,可我的程序是正确的。后来,在一次聊天模块中,通过登录账号我找到了这位“达人”,他说我们前端的确没有漏洞,他是通过自己编写的程序绕过我们前端进入到系统后端的,而我们的后端并没有进行校验。当初如果有专业的测试人员,这个Bug是有可能避免的。

众所周知,软件产品的安全性是很重要的问题,而软件测试是保证产品安全的关键所在。下面的例子说明如果没有做好软件测试,就可能造成严重的后果。有一次我所在的公司开发了一个产品,用户曾经投诉,采用我们公司的这个产品后,经常发现一些没有登录的用户也会进入系统,损毁了公司的形象,造成很大的损失。后来经过数个月的排查才发现,这是开发人员没有对SQL语句进行专门处理,由于SQL注入造成的。像这样的问题,如果在正式上线前,经过严格的测试,这个Bug是可以事先找到和解决的,这样就不会造成那么大的损失。当然,要能够测试出上面出现的Bug,是需要有一定工作经验的,只有具有丰富软件开发经验的人才能胜任,所以我一直强调从事软件测试工作前最好进行3年以上的软件开发工作。为此,在本书的有关章节中我将会进行详细阐述。

本书还将介绍一些功能测试和性能测试相互依存的例子,这源于我在某家公司做BBS系统的测试工作,系统在前4个月运行一直非常好,可后来系统显示的速度明显降低了,原来几秒钟显示一个页面,现在变成要两分钟才能显示页面。以前好评如潮,现在投诉不断。经过查找,发现这是由于当时只做了功能测试,而没有进行规范的性能测试造成的。“重功能,轻性能”,这是在软件测试工作中经常犯的一个毛病,值得引起重视。

软件测试必须对相应的业务有所了解。记得我刚到意法半导体有限公司时,从事数字电视机顶盒测试工作。这是一种嵌入式软件产品,这种类型的测试工作往往比较复杂,因为这种软件在开发初期是看不见、摸不着的,只有到后期才可以在仿真器、模拟器,甚至移植到真机中才能测试,再加上我对数字电视业务知识的缺乏,在测试中不太容易发现Bug。记得在2005年12月31日,开发人员当天下午5点才把一份软件测试版本交给我们测试人员,为了交给客户作元旦的献礼,我们必须在当天下午6点半做完测试,时间紧迫,我们只能对一些最重要的功能进行测试,又加上我对业务不太熟悉,选择重点时没有把握好,产品交付给用户在2006年元旦使用时,开始系统运行得非常好,但一个多小时后,数字电视的音量达到最大,更糟的是,根本无法用遥控器来进行操控。后来究其原因是嵌入式软件的内存空间很小,而程序中存在着野指针,所以发生了内存溢出,导致音量失控。

开发与测试之间经常是一对矛盾,这往往需要开发与测试之间进行有效沟通来解决。记得2008年上半年,我所在公司的产品进入一个开发的关键时期,开始大规模地招聘开发人员,公司40%多的开发人员都是新员工。这段时间,测试人员总能发现许多Bug,使得大部分开发人员要疲于修改这些Bug,根本没有时间去开发产品的新功能,这就导致开发人员对测试人员的意见很大,甚至有些开发人员认为测试人员是故意给他们找麻烦。当时我作为测试经理认识到测试人员与开发人员之间的矛盾必须解决,必须协调双方的关系,于是我一方面要求测试人员不但要能发现问题,还要逐步学会从Log日志中定位到问题,尽可能协助开发人员解决问题。同时我又主动和开发部门经理协商,要求开发人员在提交测试版本之前必须认真做好自测。之后,开发人员与测试人员之间关系得到改善,产品的质量也得到提高。

由于新兴的敏捷开发模式便于在相对短的迭代周期内发布一个新版本,往往几个月就可以发表一个新的版本。这就给回归测试带来很大的挑战,也促使自动化测试得到不断发展。在回归测试中,自动化测试扮演了非常重要的角色,特别是后来采用持续集成(CI)技术,自动化测试的优势得到了更好体现。当然,自动化测试也不是万能的,由于自动化测试工具本身也是软件,它也会有Bug,特别是刚开发出的自动化测试脚本,用它验证产品代码,当发现一个测试用例没有通过时,就很难判定是产品的问题还是自动化测试脚本本身的问题。另外,随着需求的变更,自动化测试脚本的更新也要随时跟进。这会使得测试人员的大部分精力都集中在调试和维护自动化测试脚本上,而不能更好地做好测试分析与设计工作。自动化测试在软件基本功能验证以及性能测试等不能用手工方法来完成的测试工作中,取得了很好的效果。但是,在一些基于经验的测试方法,如“探索式测试”“缺陷攻击法”中,大部分还是需要通过手工方法来实现。

有了一定的测试经验如果没有理论的结合,也是不完美的。例如,进行兼容性测试时,组合的对象往往很多,穷举测试是不太可能的,随机抽样测试也不靠谱。根据一种叫“正交测试法”的测试理论,可以把测试用例减少很多。另外,它有统计学的理论作为保证,其测试的可靠性也得到提高。这说明,由于IT行业发展十分迅速,从事软件测试的工作者也要与时俱进,不断学习新的理论和方法。

以上只是本书中的一小部分,我把十几年在软件测试中的实践、体会和思考总结成书,希望为读者打开一扇通往软件测试之路的大门,使读者寻找到解决测试问题的技术和方法,体验到测试工作中“逮”Bug犹如“寻宝”的乐趣。本书可供软件测试同仁借鉴。由于现在许多大学里,计算机专业都开设了软件测试课程,所以本书也可作为大专院校计算机软件专业学生的参考书。

全书分为“设计”“工具”和“管理”3篇,共14章,每个章节之间虽有一定的联系,但也可各自独立成章,读者可以根据自己的需求,按照书的内容顺序阅读,也可以根据自己的兴趣选取相关章节阅读。

最后,感谢人民邮电出版社张涛先生及其编辑团队、51Testing编辑严代丽对本书的出版做出的辛勤劳动,没有你们的大力支持,出版本书的愿望是不可能实现的;感谢微信平台,它将我与全国的软件测试爱好者连接起来,共同分享软件测试给大家带来的喜怒哀乐,让大家能够利用这个平台分享软件测试的经验、思想和方法,进一步丰富本书的内容;在这里特别感谢杨艳艳、叶微、刘琛梅、赵明、刘莎莎、万巧、张晓丽、陈佳丽、詹露、张子繁、金鑫、冯昌、帅敏、沈晓静、赵院娇和蹇辉在出版后期对本书进行了仔细的校对。另外,我还要感谢我的家人对我这次出书工作在精神、物质及生活上的支持。祝愿软件测试行业能够在中国得到更好发展,有更多的测试专家能够在中国出现。本书的全部附录、代码以及探索式测试课程均可扫描下面的二维码从网站上下载。

由于本人水平以及时间有限,书中难免存在错误或者不足之处,请广大读者不吝指正。我的E-mail:xianggu625@126.com,微信号:xianggu0625。个人网站:www.3testing.com。编辑联系与投稿邮箱为zhangtao@ ptpress.com.cn。


如何把用户的需求转换为软件测试设计,这是软件测试工程师的工作重心所在。本书第一篇通过6个章节来讲述一下如何进行软件测试设计。

本篇共分以下几个章节。


本章主要介绍软件测试的基本知识,共分3节。通过本章,可以学到。

学习本章可能比较枯燥,里面有许多关于软件测试的名词定义,但是这是软件测试的基础,如果你是软件测试的菜鸟,可能有许多地方看不懂,没有关系,千万不要紧张,你可以先浏览一下,看完这本书的其他章节再回过头来重新阅读,可能会有更深入的体会。另外,我也尽可能结合一些案例来介绍这些概念。

软件测试的基本理论是软件测试的基础。在本书的开始,我们先学习和回顾一下软件测试的基本术语,这样可便于更深入地探讨软件测试的其他知识。

关于软件测试的定义有很多,这里主要介绍以下几个。

图1-1 验证与确认

1.冒烟测试(Smoking Testing)

在大部分软件测试工作中,单元测试与集成测试是由开发工程师完成的,而系统测试是由软件测试工程师完成的。为了提高软件测试工程师测试的有效性,当软件测试工程师拿到开发工程师提交的版本后,就需要进行一次冒烟测试。冒烟测试主要指测试软件版本中的主要功能是否实现,速度很快,一般一到两个小时即可完成。夸张地说,抽一根香烟的时间就可以完成测试。还有一个说法来源于硬件测试,一般硬件组装完毕,上电后,如果电路出现冒烟故障,则不必进行更深入的测试。在软件测试中,如果冒烟测试没有通过,就需要返回给开发工程师重新修改后再测试。

2.回归测试(Regression Testing)

回归测试又称衰竭性测试。为了确保修改或增加的功能没有给软件其他未改变的(或者以前测试通过的)部分带来影响,软件测试工程师进行每轮测试时,需要对先前测试过的模块再进行测试,这种测试称为回归测试。回归测试最好使用自动化软件测试工具来实现。关于回归测试,如图1-2所示。

图1-2 修改部分与新功能对原有功能的影响

3.白盒测试(White Box Testing)

白盒测试是通过分析组件/系统的内部结构进行的软件测试。白盒测试用例分析方法包括语句覆盖、分支覆盖、条件覆盖、条件/分支覆盖和路径覆盖等技术。白盒测试也可以在系统测试中进行(关于白盒测试的方法,本篇2.6节会详细介绍)。

4.黑盒测试(Black Box Testing)

黑盒测试是基于系统功能或非功能规格说明书来设计或者选择测试用例的技术,它不涉及软件内部结构。也就是说,测试工程师不需要了解程序内部是如何实现的,只需考虑输出内容的特性对应输入内容的要求。黑盒测试也可以基于代码来实现,如通过输入函数的参数和返回值来了解被测函数的功能是否得到实现。

案例1-1:函数级别黑盒测试。

函数float calcualteSimilty(String a, String b),返回0.00~1.00小数点精度为两位的浮点数。比较字符串a,b的相似程度,0.00表示一点不相似,1.00表示完全相似。可以用下面的测试用例来实现函数级别的黑盒测试,特别声明,这里不需要了解函数内部是如何实现的,只关心函数输入与输出的对应关系。测试用例如下:

5.单元测试(Unit Testing)

单元测试又称组件测试,是对单个软件组件进行的软件测试【与IEEE610一致】。单元测试一般采用软件测试驱动与桩的技术。

案例1-2:单元测试。

对图1-3的模块B进行单元测试如下。

图1-3 单元测试

 B模块的代码可能如下:

int B (int a, int b){
     …
     int x= D (a);
     ….
     int y= E(b);
    ….
     return x+y;
}

B的驱动函数是指通过页面或者编译器可以调用的函数,通常设置为主函数,即main()函数。

main (){
    int a=3;
    int b=5;
    int c=B(a,b);
}

StubD,StubE为B的桩函数。桩函数为模拟被测单元的调用模块,由于测试的是模块B,所以桩函数可以简单地返回一个符合要求类型的变量。

int  StubD(int x){
    return x+5;
}
int  StubE(int x){
    return x+6;
}

这样,B函数就可改为:

int B (int a, int b){
     ….
     int x= StubD (a);
     ….
     int y= StunE(b);
    ….
     return x+y;
}

这里,单元测试主要验证模块B的功能,在验证过程中可以采用等价类、边界值、错误输入等方法来实现。

对于软件测试桩,现在有许多新的技术,如图1-4所示。

图1-4 软件测试桩

这些新技术的介绍不在本书的范畴中,有兴趣的读者可以自己查找相关的文献。

6.集成测试(Integration Testing)

集成测试是一种暴露接口以及集成组件/系统间交互时存在缺陷的软件测试方法。集成方法有自上而下测试法、自下而上测试法、自上而下和自下而上混合(又称三明治)测试法3种。

案例1-3:集成测试。

下面来看一个程序架构,如图1-5所示。

可以采取如下方法对此进行集成测试。

(1)模块6与模块7集成,模块6与模块8集成;

(2)模块3与模块5集成,模块3与模块6集成;

(3)模块2与模块4集成,模块2与模块5集成;

(4)模块1与模块2集成,模块1与模块3集成。

图1-5 集成测试案例

(1)模块1与模块2集成,模块1与模块3集成;

(2)模块2与模块4集成,模块2与模块5集成;

(3)模块3与模块5集成,模块3与模块6集成;

(4)模块6与模块7集成,模块6与模块8集成。

(1)模块6与模块7集成,模块6与模块8集成;

(2)模块3与模块5集成,模块3与模块6集成;

(3)模块1与模块2集成,模块1与模块3集成;

(4)模块2与模块4集成,模块2与模块5集成;

(5)模块3与模块5集成,模块3与模块6集成。

7.系统测试(System Testing)

系统测试是软件测试的主要部分,是利用各种方法验证软件是否满足产品显性或者隐形需求的活动。

8.验收测试(Accept Testing)

验收测试一般由用户/客户或者运维人员进行确认是否可以接受一个系统的验证性的软件测试。可根据用户需求、业务流程进行的正式的软件测试,以确保系统符合所有验收准则(与IEEE 610一致)。验收测试可以分为Alpha测试和Beta测试。

(1)Alpha测试

Alpha测试是由潜在用户或者独立的软件测试团队在开发环境下或者模拟实际操作环境下进行的软件测试,通常在开发组织外进行,是对现货软件(off-the-shelf software)进行内部验收测试的一种方式。

(2)Beta测试

Beta测试是潜在现有用户/客户在开发组织外的场所,没有开发工程师参与的情况下进行的软件测试,检验软件是否满足客户及业务需求。这种软件测试经常是为了获得市场反馈对现货软件进行外部验收测试的一种形式。

9.静态测试(Static Testing)

静态测试是对组件/系统进行规格或实现级别的测试,但并不执行这个软件,如代码评审或静态代码分析等。

10.动态测试(Dynamic Testing)

动态测试通过运行软件的组件或系统来测试软件。

更多的软件测试术语,请参见参考文献【33】。

讨论软件测试学,不得不涉及软件工程模型,因为软件测试学与软件工程学的发展是依依相关、相辅相成的。根据目前比较先进的软件测试理念,软件测试应该贯穿于软件工程的整个过程中。下面介绍几种软件工程模型。

1.瀑布模型

图1-6为瀑布模型。这个模型是最经典的软件工程模型,包括“计划”->“需求分析”->“设计”->“编码”->“测试”->“运行维护”这几个阶段。

图1-6 瀑布模型

但是,这个模型存在比较严重的缺点。

(1)不可反复及不适用于需求变更比较频繁的情况。由于瀑布模型从业务建模到运行维护一脉相承,不可以反复。而现代软件项目中,需求变更是无处不在的:“唯一不变的是需求变更”。若运用这种模型,只要项目需求发生变化,就要把原有的设计打翻,重新进行系统分析,概要设计,详细设计等。

(2)用户很难在项目初期了解项目状态:由于用户在项目初期很难提出明确的需求,而利用瀑布模型只有到编码结束,软件测试工程师才可介入软件测试,客户才可以看到是否是他们需要的产品,在此之前这些产品他们不完全了解,有时需要补充,有时客户也有可能推翻他们原本的需求,提出新的需求,这样往往会给客户方、开发方带来很多麻烦。

2.迭代模型和螺旋模型

图1-7为迭代模型。瀑布模型和迭代模型往往在概念上区别不明显。事实上,这两个模型在思想本质上是一致的。它将客户的需求按照用户的重要等级和模块自身的等级进行安排,从最开始进行分析、设计、编码、测试,然后再进入下一轮迭代。用户只要在每一轮结束后,就可以看到产品的一些雏形,从而可以进行需求变更和提出下一轮建议。该模型初期开发工作比较少,用户又可以及时提出下一轮更详细的需求和变更,所以这样的模型往往利于软件公司产品的研发。这类模型有著名的RUP模型、快速开发模型以及现在比较流行的敏捷开发等,它们都遵循迭代的思想。

图1-7 迭代模型

扩展阅读:增量开发与迭代开发

 

增量开发

迭代开发

注:本书中扩展阅读大部分来自于百度百科,请见参考文献【21】。

1.V模型

图1-8所示为V模型测试。

图1-8 V模型测试

2.W模型

图1-9所示为W模型测试。W模型其实是V模型的变种,它提倡的主要思想是软件前置测试理念(即软件测试需要贯穿软件研发的始终)。所以,W模型又称双V模型或前置模型。在需求、设计和编码阶段对产生的工件进行文档评审,一个目的是提出自己的建议和意见,另外一个目的是尽可能理解产品的需求和实现方式。使用前置软件测试法,Bug在软件前期就可以发现,从而降低软件开发的成本。

图1-9 W模型测试

3.X模型

图1-10为X模型测试。X模型将软件系统分为若干模块,对每个模块进行单元测试、集成测试以及系统测试,然后统一对模块进行集成测试。事实上,这里已经提出了“探索式软件测试”的概念,在本书第3章会详细介绍探索式测试。

图1-10 X模型测试

 

程序员在编程的时候必须定义程序用到的变量,以及这些变量所需的计算机内存,这些内存用比特位来定义,如int16、int32、double、float等。

一个16位的整数变量可以代表-32.768到32.767中间的值。而一个64位的整数变量可以代表-9.223.372.036.854.775.808到9.223.372.036.854.775.807中间的值。

1996年6月4日上午9时33分59秒,随着5、4、3、2、1、0的倒计时,阿丽亚娜五型运载火箭的首次发射点火后,火箭开始偏离路线,最终被逼引爆自毁,整个过程只有短短的30s。阿丽亚娜五型运载火箭是基于前一代四型火箭开发的。在四型火箭系统中,对一个水平速率的测量值使用了16位的变量及内存,因为在四型火箭系统中反复验证过,这一值不会超过16位的变量,而五型火箭的开发工程师简单复制了这部分程序,而没有对新火箭进行数值的验证,结果发生了致命的数值溢出。发射后这个64位带小数点的变量被转换成16位不带小数点的变量,引发了一系列的错误,从而影响了火箭上所有的计算机和硬件,瘫痪了整个系统,因而不得不选择自毁。

阿丽亚娜五型载火箭使用Ada语言开发,出问题的代码如下:

在这个代码中导致最终问题的是最后一句。在这一段语句中共有7个变量运算符出现了问题,仅有4个做了异常处理的保护,而其他3个没有进行。但是这也是由于运行的机器SRI计算机中设定最大负荷目标值为80%,如果要进行异常处理,计算机的CPU要处理的代码会增多。

教训:软件设计和Code Review的重要性。另外阿丽亚娜五型运载火箭在倒计时阶段、飞行阶段以及进入轨道阶段都未经过测试验证。

软件测试方法见表1-1。

表1-1  软件测试方法

白盒

黑盒

动态

利用KDE的调试功能逐步调试程序,进行软件测试

通过人工或者自动方法进行软件测试

静态

代码评审

对需求、设计等文档进行审核

代码评审中有一个部分是对编码规范的检查。另外,代码评审可以通过人工的方式来实现,也可以借助代码评审工具,如在本书第二篇7.1.1节“普通软件测试工具推荐”提及的Checkstyle、Findbugs、PMD、Android Lint等工具。

扩展阅读:阿丽亚娜五型运载火箭的爆炸-代码静态测试的重要性

L_M_BV_32:=TBD.T_ENTIER_32S  ((1.0/C_M_LSB_BV) * (G_M_INFO_DERRIVE());
if L_M_BV_32 >32767 then
P_M_DERIVE(T_ALG.E_BV) :=16#7FFF#;
elseif L_M_BV_32 <-32767 then
P_M_DERIVE(T_ALG.E_BV) :=16#8000#;
else 
P_M_DERIVE(T_ALG.E_BV):=UC_16S_EN_16NS(TDB.T_ENTIER_16S(L_M_BV_32));
end if;
P_M_DERIVE(T_ALG.E_BH):=UC_16S_EN_16NS(TDB.T_ENTIER_16S(1.0/C_M_LSB_BH)*G_M_INFO_DRIVER(T_ALG.E_GH)));

图1-11描述了软件测试步骤,具体如下。

图1-11 软件测试步骤

(1)软件测试计划。

(2)软件测试分析。

(3)软件测试设计。

(4)软件测试实施。

(5)软件测试执行。

(6)评估出口准则和报告。

(7)软件测试结束活动。

具体内容读者可以参见参考文献【13】第二章进行更深入的学习。

1.缺陷管理流程

根据SEI TSP国际标准,缺陷管理流程可以定义如下。

研发计算机必须分为开发机、测试机和发布机。开发工作在开发机上进行,软件测试工作(系统测试)在测试机上运行,最后产品验收和运行在发布机上运行,发布机器可能在客户处。

(1)每轮测试开始,开发部门提出本次测试重点,开发机上的版本同步到软件测试机上(或通过配置管理工具实现同步)。

(2)软件测试工程师进行冒烟软件测试,如果冒烟测试没有通过,则退回给开发部门,等待开发部门重新提交软件测试任务,返回第(1)步。

(3)冒烟测试通过,测试工程师继续执行测试活动,包括传统正规测试和基于经验的测试,如探索式软件测试等。发现Bug,记录在缺陷管理工具中。

(4)开发工程师修改被确认的Bug(状态为Assigned)。

(5)当软件测试工程师认为软件测试结束,大部分Bug都发现完毕,开发机上版本再一次同步到软件测试机上。

(6)软件测试工程师对Bug进行复测,如果问题仍旧存在,则标记为Reopen,否则标记为Closed。此时还要对以前测试过的功能进行回归测试。

(7)开发工程师对于Reopen的缺陷进行修改。

(8)当一轮软件测试达到出口标准,软件测试机上的版本同步到发布机上,软件测试任务完成;否则返回第(5)步。

在本书第三篇第13.9节“软件缺陷管理流程”会给出更为详细的描述。

2.缺陷严重等级

由于采用的缺陷管理工具不同,缺陷严重等级的级别也会有差异。

 Blocker:(阻碍的)

 Critical:(紧急的)

 Major:(重大的)

 Normal:(普通的)

 Minor:(较小的)

 Trivial:(外观的)

 Enhancement(改进的)

3.缺陷修改优先级

 由于缺陷管理工具的差异,缺陷修改优先级别也会有差异。

 P5:严重级别比较高,影响软件测试进行或者系统无法继续操作。

 P4:对系统操作有影响,但不需要马上修改。

 P3:页面缺陷(不属于定义的缺陷范围)或者建议。

 P2:准备在下一轮软件测试前修改完毕。

 P1:准备在下一版本中修改。

4.缺陷书写规则

缺陷编号:【一般缺陷管理工具自动生成】

缺陷简要描述:【一句话描述】

发现者:【一般从下拉框中选择】

修改者:【一般从下拉框中选择】

最早发现所在版本号:【一般从下拉框中选择】

最早发现日期:【一般由日期框选择】

最早修改日期:【一般由日期框选择】

缺陷当前所在模块:【一般从下拉框中选择】

缺陷当前状态:【一般系统自动生成】

缺陷发现时系统环境:【文本框输入或者下拉框选择】

缺陷重现步骤:【由缺陷发现者填写】

实际得到结果:【由缺陷发现者填写】

期望得到结果:【由缺陷发现者填写】

修复描述:【由缺陷修复者填写】

相关文件:【由缺陷发现者填写】

延迟/不修改/修复/回退原因说明:【由缺陷负责人填写】

历史信息:【由缺陷管理系统自动生成,包括状态迁移,所经过的人,各阶段描述等信息】

附件:【由缺陷发现者上传文件】

关于缺陷管理工具将在本书第二篇第10章“缺陷管理工具”进行详细描述。

扩展阅读:世界上第一个Bug

 

1947年9月9日下午3点45分,Grace Murray Hopper在她的记录本上记下了第一个计算机Bug——在Harvard Mark II计算机里找到的一只飞蛾,她把飞蛾贴在日记本上,并写道“First actual case of Bug being found”。这个发现奠定了Bug这个词在计算机世界的地位,变成无数程序员的噩梦。从那以后,Bug这个词在计算机世界表示计算机程序中的错误或者疏漏,它们会使程序计算出莫名其妙的结果,甚至引起程序的崩溃。Grace Murray Hopper是历史上最早一批程序员,而且还是个女程序员。

Hopper的记录连同那只飞蛾现在存在美国历史博物馆。

1.测试用例格式

测试用例格式见表1-2。

表1-2 测试用例格式

编号 Chinafi\_\*\*\*\_XXX
前置条件
说明
项目编号 测试步骤 期待结果 概要说明
1 1
2
3
4
5
2 1
2
3
4
5
6
7
8
9

 

2.测试用例案例

案例1-4:测试用例的书写。

环境:浏览器、Web服务器(Tomcat)、MySQL数据库。

需求:一个表单信息,用于网站用户注册个人信息,主要包括姓名、登录名、密码(大于5个字符,必须包含数字和特殊字符)、确认密码、Email信息、手机、地址,其中登录名、密码、确认密码、Email信息是必填的,其他信息可以选填。请根据需求   书写测试用例(不考虑长度测试)。用户注册界面如图1-12所示,用户注册测试用例见表1-3。

图1-12 用户注册界面

表1-3 用户注册测试用例

编号 zmn_reg_002
前置条件 注册模块冒烟测试通过
说明 测试系统注册功能
项目编号 测试步骤 期待结果 概要说明
1 1.进入系统
2.点击“注册”链接
3.对所有输入项输入正确的信息
4.单击【注册】键 注册成功,查看数据库中数据正确,并且能够正常登录
2 1.进入系统
2.点击“注册”链接
3.输入已经存在的用户名 系统提示,该用户名已经被注册
3 1.进入系统
2.点击“注册”链接
3.密码不包含数字和特殊字符 系统提示,密码必须包含数字和特殊字符
4 1.进入系统
2.点击“注册”链接
3.密码不包含特殊字符 系统提示,密码必须包含数字和特殊字符
5 1.进入系统
2.点击“注册”链接
3.密码不包含数字 系统提示,密码必须包含数字和特殊字符
6 1.进入系统
2.点击“注册”链接
3.密码与确认密码不匹配 系统提示,密码与确认密码不匹配
7 1.进入系统
2.点击“注册”链接
3.输入非法格式的Email 系统提示,Email格式非法
8 1.进入系统
2.点击“注册”链接
3.输入非法格式的手机号码 系统提示,非法号码
9 1.进入系统
2.点击“注册”链接
3.单击【注册】键 系统提示,登录名、密码、确认密码、Email为必填项,用红色字显示
10 1.进入系统
2.点击“注册”链接
3.对所有输入项输入正确的信息
4.单击【注册】键 注册成功,查看数据库中数据正确,并且能够正常登录
5.单击浏览器上的【刷新】键 注册信息没有被再次提交,并且显示友好信息给用户
11 1.进入系统
2.点击“注册”链接
3.试图对某一到多个字段进行XSS注入 注入信息方法和验证查看《安全测试手册》
4.单击【注册】键 注入失败
12 1.进入系统
2.点击“注册”链接
3.试图对某一到多个字段进行SQL注入
4.单击【注册】键 注入失败 注入信息方法和验证查看《安全测试手册》

当然,要写好测试用例,首先要学好如何进行测试设计,后续章节中会进行详细介绍。

关于软件测试类型,可以参照ISO 225000(替代ISO 9126)软件质量模型,如图1-13所示。

图1-13 ISO225000(替代ISO 9126)软件质量模型

1.功能测试

功能测试对测试对象侧重于所有可直接追踪到用例或业务功能和业务规则的软件测试需求。这种软件测试的目标是核实数据的接收、处理和检索是否正确,以及业务规则的实施是否恰当。此类软件测试可以通过黑盒测试技术或白盒测试技术来实现,该技术通过图形用户界面(GUI)或其他方式与应用程序进行交互,并对交互的输出或结果进行分析,以此核实应用程序及其内部功能。

案例1-5:功能测试。

图1-14所示是电子商务计价系统界面。随着电子商务网站越来越多,某些商品在节假日可以打折,会员可以享受会员价,购买物品达到一定数量或金额后,也可以打折或者免运费。这些条件给计价系统的准确性带来很复杂的功能,软件测试工程师应该设计好各种测试用例,来检测系统的功能。

图1-14 电子商务计价系统界面

2.易用性测试(用户体验性测试)

易用性测试指的是在指定条件下使用时,软件产品被理解、学习、使用和吸引用户的能力。

(1)操作人员。

(2)最终用户。

(3)受该软件的使用影响或者依赖于该软件的间接用户。

案例1-6:易用性测试。

如图1-15所示,对于安卓系统卸载APP软件,必须进入设置界面,找到软件,再单击【卸载】按钮才可以卸载;而苹果系统只要在界面上长按APP软件图标3s,点左上角的叉,即可删除。由此可见,苹果系统的卸载APP的软件易用性明显优于安卓系统。另外,现在我们给易用性测试起了一个更好听的名字,叫“软件用户体验性测试”。

图1-15 安卓系统与苹果系统的卸载APP功能

3.可靠性测试

可靠性测试的目的之一是对软件成熟度在时间上的统计度量指标进行监控,并将其与既定目标比较。可靠性对应3个指标,如图1-16所示。

图1-16 软件可靠性

 (1)平均失效间隔时间MTBF(这次失效到下次失效的时间)。

 (2)平均修复时间MTTR(本次失效修复的时间)。

 (3)平均失效前时间MTTF(修复完毕到下次失效的时间)。

通过图1-16所示,可以看出:MTBF=MTTR+MTTF。

另外,可靠性失效指标的一般公式:可靠性失效指标=MTTR/MTBF×100%

案例1-7:电信系统软件的可靠性。

在电信领域,可靠性失效指标要达到著名的5个9,即99.999%,也就是说一年中允许设备故障的时间为:365×(1−99.999%)天=8760×(1−99.999%)小时=525600×(1−99.999%)分钟=5.256分钟。

4.性能测试

性能测试的类型比较多,这里主要考虑以下3种类型。

(1)基本性能测试:正常情况下软件的响应速度。

(2)负载测试(LOAD测试):通过增加负载(一般为并发用户或数据库容量)来评估组件或系统性能的软件测试方法。

测试方法:以一定的负载作为起点,观察系统吞吐率,不断加大负载个数,直到吞吐率达到饱和,这时负载为该产品这个功能的最大负载。

(3)压力测试:评估系统处于一定的负载下(最大负载乘以一定百分比),让系统运行一段时间,观察系统各项指标是否正常。

案例1-8:Web系统的性能测试。

在Web页面对用户登录功能进行负载测试,获取最大负载数,并以最大负载的80%,持续运行48小时进行压力测试,观察系统各项指标是否正常运行。

关于性能测试,本篇第5.1节将会详细讲解。

5.安全性测试

软件安全性包括功能安全性和信息安全性,本节只考虑信息安全性。

信息安全性:指的是软件产品保护信息和数据的能力,及未授权的人员或者系统不能阅读或者修改这些信息和数据,而不拒绝授权人员或者系统对它们进行访问。信息安全性测试的关注点:

信息安全性分类:

信息安全性测试方法:

案例1-9:黑客侵入。

某公司开发一套网上答题系统,题目均为单项选择题,可以选择A、B、C、D中的任意一项,每一周评选最高得分者,可以在电视节目中参加一个益智类的栏目。为了防止网友对所有题选择某个相同的答案(如对所有题都选择D),或者有规律的选择(如选题都是A、C、D、B、A、C、D、B…)在前端JavaScript程序里做了控制:如果连续5次选择同一个答案或者有规律地选择的答题者将被答题系统自动踢出。该程序经过严格测试后上线使用。可是,上线不到4周,发现每周最高得分者均为一个姓张的先生,查看其答案,竟然所有题目都答成B,这让开发工程师感到很奇怪。两周后,公司的开发经理在网站群聊中找到这位张先生,张先生告诉开发经理,系统在前端JavaScript做了控制,但是在后端JavaBean中没进行控制,所以他自己写了个程序绕过前端,这个程序是一个死循环,7×24小时一直发送答案B给后端系统。

案例1-10:XSS注入。

如果没有对HTML特殊字符进行处理(HTML特殊字符见附录A),在浏览页面时会运行JavaScript代码,如果输入的JavaScript代码具有恶意获得用户信息的功能,就会产生安全问题,如输入:“ <script type="text/javascript">var sys = getBrowserInfo();document.write (sys.browser + "的版本是:" + sys.ver);</script>”,页面在显示时就会把用户当前的浏览器版本和型号都显示出来。这样,黑客就可以根据获取的信息采取进一步攻击。

案例1-11:SQL注入。

SQL注入比XSS注入更加危险。下面的例子可以造成用户不注册就能登录系统:下面是登录系统的SQL语句:select count(*) from user where name='$name' and password='$password'。上面是用户登录的SQL语句,如果count(*)不为零,用户即可进入系统。$name,$password为用户在界面中输入的值,这里作为一个变量存储。$name可以任意输入,如输入“Jerry”,$password输入类似于“2222' or 1=1;-- '”,由于这样SQL语句变为select count(*) from user where name='Jerry' and password='2222' or 1=1;-- ',where语句后的条件永远为真,所以判断语句count(*)一定不为零。

6.相容性测试

相容性测试又称兼容性测试,指的是软件产品与一个或者多个规定的系统之间进行交互的能力。该项测试用于验证软件产品或者应用程序在各种指定的目标环境下是否可以正常工作,主要包括:

(1)硬件;

(2)软件;

(3)中间件;

(4)操作系统;

(5)其他。

兼容性测试包括:输入的兼容性、输出的兼容性以及自适应性。

案例1-12:设备接口兼容性。

某些设备厂商生产出的产品需要被其他厂商调用,或者调用其他厂商的接口。在这些厂商中,北向接口与南向接口经常被提及。北向接口和南向接口如图1-17所示。

图1-17 北向接口和南向接口

可以看出,如果用单元测试做一个比喻,北向接口设备相当于驱动函数,而南向接口设备相当于桩函数。

案例1-13:屏幕分辨率测试。

屏幕分辨率测试属于兼容性测试的范畴,要求测试在不同屏幕分辨率下。界面的美观程度,可分为800×600、1024×768、1152×864、1280×768、1280×1024、1200×1600等,不同字号下的测试。

7.可移植性测试

可移植性测试通常和软件移植到某个特定的运行环境中的难易程度相关,包括第一次建立或从现有环境移植到另一个环境。这种测试类型包括:

(1)可安装性测试;

(2)适应性测试;

(3)可替换性测试。

案例1-14:网络设备移植测试。

某软件从网络设备A移植到网络设备B中,发生了错误。后经过排查,结论是网络设备A的IP地址用的是用户地址序列(高位在前,低位在后)。而网络设备B的IP地址用的是网络地址序列(低位在前,高位在后)。如IP地址是192.168.0.8,转化为十六进制为C0.A8.00.08,在设备A上是用户地址序列为C0A80008。在设备B上是网络地址序列为0800A8C0。

故障转移和恢复测试属于可移植性测试范畴,它可确保软件测试对象能成功完成故障转移,并能从意外数据损失或数据完整性破坏的各种硬件、软件或网络故障中恢复。

安装、卸载测试也属于移植性测试,安装测试有两个检查点。

(1)确保该软件在正常情况和异常情况的不同条件下(如进行首次安装、升级、完整的或自定义的安装)都能进行安装。异常情况包括磁盘空间不足、缺少目录创建权限等。

(2)核实软件在安装后可立即正常运行。

卸载测试有4个检查点:

(1)卸载是否正常、卸载后的软件是否能够运行;

(2)核实卸载软件的数据与文件都删除干净;

(3)卸载后的软件重新安装是没有问题的;

(4)卸载后的软件不影响其他软件的工作。

8.可维护性测试

可维护性测试指的是软件产品可被修改的能力,包括纠正、改进或者软件对环境、需求和功能规格说明变化的适应能力。

案例1-15:代码可维护性测试。

某公司生产了ERP产品给A企业,3年后由于公司ERP流程发生变化,需要在原来基础上进行更新,但是由于3年来近一半的开发工程师发生了变动,代码注释又不规范,给新功能开发带来很大困难,这就产生了代码可维护性的问题。为了解决这个问题,软件工程师把代码进行了如下优化,如图1-18所示。

图1-18 代码的可维护性

 要做好代码的可维护性,最好是在编码后期做好严格的代码审核(Code Review)工作。

案例1-16:产品的可测试性。

某B/S产品决定采用WebDriver进行测试,由于HTML代码中的元素都没有id、name或者class属性,如:

<input type="button" value="点击">

如果采用手工测试,是没有关系的,但是采用自动化测试,就带来很大困难,于是把HTML代码改为:

<input type="button" value="点击" name="click" id="my_click">

关于WebDriver的介绍参看本书第二篇第11.2节“Selenium和WebDriver工具入门”介绍。

在软件测试工作中除了关注ISO 22500标准外,我们还经常用到以下测试方法。

9.数据和数据库完整性测试

在项目名称中,数据库和数据库进程应该作为一个子系统来进行软件测试。测试这些子系统时,不应将测试对象的用户界面用作数据的接口。对于数据库管理系统(DBMS),需要进行深入研究,以确定可以支持以下测试的工具和技术。数据库测试包括以下几个方面。

扩展阅读:FIPS标准和SQL-92标准

 

1.FIPS标准

FIPS(Federal Information Processing Standards)即(美国)联邦信息处理标准。它是批准技术与标准国家协会 (National Institute of Standards and Technology),为联邦计算机系统制定标准和指南。

2.SQL-92标准

SQL-92,是数据库的一个ANSI/ISO标准。

SQL92标准有4个层次

  • 入门级

这是大多数开发商符合的级别。这一级只是对前一个标准SQL89稍做修改。所有数据库开发商都不会有更高的级别,实际上,美国国家标准和技术协会NIST(National Institute of Standards and Technology,这是一家专门检验SQL合规性的机构)除了验证入门级外,甚至不做其他的验证。Oracle 7.0于1993年通过了NIST的SQL92入门级合规性验证,那时我也是小组中的一个成员。如果一个数据库符合入门级,它的特性集则是Oracle 7.0的一个功能子集。

  • 过渡级

   这一级在特性集方面大致介于入门级和中间级之间。

  • 中间级

   这一级增加了许多特性,包括(以下所列并不完整):

  • 动态SQL;
  • 级联DELETE以保证引用完整性;
  • DATE和TIME数据类型;
  • 域;
  • 变长字符串;
  • CASE表达式;
  • 数据类型之间的CAST函数。
  • 完备级

  增加了以下特性(同样,这个列表也不完整):

  • 连接管理;
  • BIT串数据类型;
  • 可延迟的完整性约束;
  • FROM子句中的导出表;
  • CHECK子句中的子查询;
  • 临时表。

入门级标准不包括诸如外联结(outer join)和新的内联结(inner join)语法等特性。过渡级则指定了外联结语法和内联结语法。中间级增加了更多的特性,当然,完备级就是SQL-92全部。有关SQL-92的大部分书都没有区别这些级别,这就会带来混淆。这些书只是说明了一个完整实现SQL-92的理论数据库会是什么样子。所以无论你拿起哪一本书,都无法将书中所学直接应用到任何SQL-92数据库上。关键是,SQL-92最多只达到入门级,如果你使用了中间级或更高级里的特性,就存在无法“移植”应用的风险。

10.本地化测试

本地化测试是指为各个地方开发产品的软件测试,如英文版、中文版等,包括程序是否能够正常运行,界面是否符合当地习俗,快捷键是否正常起作用等,特别要测试在A语言操作系统环境下运行B语言软件(如在英文版的Windows操作系统下试图运行中文版的程序),运行是否正常。

11.文字测试

文字测试主要测试文字是否拼写正确、是否易懂、不存在二义性、没有语法错误;文字与内容(包括图片、文字)是否有出入等。

12.发布测试

主要在产品发布前对一些附带产品,如说明书、广告稿等进行软件测试。发布测试在验收测试中进行。

说明书测试

说明书测试主要为语言检查、功能检查、图片检查。

宣传材料测试

主要测试软件产品中附带的宣传材料中的语言、描述功能、图片。

产品说明书的测试

产品说明书是用户(特别是一些新用户)了解产品的一个有力工具。所以,软件测试工程师应该对产品说明书中的每一条功能进行严格核实。除此之外,还应从用户的角度思考,考虑是否将注意事项告诉了用户,产品说明书是否便于阅读,产品说明书的书写逻辑是否合理以及说明书中章节的前后顺序是否需要进行调整。

产品广告

产品广告往往是由市场人员为了推销产品而书写的,对于广告中提及的功能,我们要与市场和销售人员进行及时沟通,弄清楚每条语句是在哪个模块的哪个功能点上实现的,然后在产品上具体操作一下,看是否是那么一回事。广告具有一定的夸大性,这在所难免。但是,对于夸大过份的内容,软件测试工程师有提出修改建议的责任。

众所周知软件的Bug不可能为零,但一般随着时间的推移,Bug数逼近于零。软件测试曲线如图1-19所示。

图1-19 软件测试曲线

 这里,横坐标是时间,纵坐标是还没有发现的Bug数。项目开始前,Bug为无穷大,随着时间的推移,Bug趋于零,但是不会等于零。

另外一条曲线的横坐标是时间,纵坐标是已经发现的Bug数。项目开始前,Bug为零,随着时间的推移,Bug趋于一个固定值,但是不会等于这个值。

一般来说,两条曲线的交汇处为产品发布的最好时候,避免过度软件测试,也避免软件测试不够。

由于每个软件测试工程师的思路不同,测试的侧重点也可能不同,所以,不同的测试工程师即使执行相同的测试用例,发现的Bug也可能不同。例如,A测试某个模块,第一天到第四天测到许多Bug,但是从第五天开始几乎报不出Bug了。第七天换了B,B又测试出许多Bug,但不能简单地说A的水平差,B的水平高。其实,这是由于A对这个模块产生了抗药性造成的,这就是软件测试学中的杀虫剂现象,可用图1-20表示。

图1-20 软件测试的杀虫剂现象

为避免杀虫剂现象,建议每次进行轮流测试,最好安排不同的工程师进行不同模块的测试工作。

案例1-17:根据软件杀虫剂现象进行测试计划调整。

某软件项目有测试员甲、乙、丙、丁4人,项目模块为A、B、C、D、E、F、G七个模块,测试周期为3周,为了避免软件杀虫剂现象,测试经理做了分工,见表1-4。这样保证了每一个模块至少有两个人经过测试。

表1-4  工作任务的分工

第一周

A、C

B

D、E、G

F

第二周

B

A、C

F

D、E、G

第三周

D、E、G

F

A、C

B

下面是业界公认的软件测试的七项原则。

软件测试可以显示软件中存在缺陷,但不能证明软件不存在缺陷。软件测试可以减少软件中存在未被发现缺陷的可能性,但即使软件测试没有发现任何缺陷,也不能证明软件或系统是完全正确的。软件中到底存在多少缺陷,谁也不知道。软件测试的目的是尽可能发现更多的缺陷。此外,有些缺陷是不影响使用的,所以在考虑时间和成本上可以不必修改,从而防止过度测试带来对资源的浪费。

进行完全(各种输入和前提条件的组合)的软件测试是不可行的。通过运用风险分析和不同系统功能的软件测试优先级,确定软件测试的关注点,从而替代穷尽软件测试。穷尽软件测试真正的意思是,在软件测试完毕后,软件测试工程师知道在系统里没有残留任何未知的Bug。因为如果有未知的Bug,那么就可以通过做更多的软件测试找到他们,这样软件测试也就还没有穷尽。因为零缺陷的软件是不存在的,所以穷尽的软件测试也是不可行的。

扩展阅读:Good enough原则

 

软件测试的原则是Good-enough原则:这是一种权衡投入/产出比的原则,测试既不要不充分,也不要过分,不充分和过分都是一种不负责任的表现,当然达到Good enough是一种理想状态。

为了尽早发现缺陷,在软件或系统开发生命周期中,软件测试活动应该尽可能早地介入,并且也应该将关注点放在已经定义的软件测试目标上。在软件测试的各个阶段,软件测试最好在需求分析期间就介入进去,一方面可以尽早发现缺陷,另一方面可以尽早掌握产品的需求和设计,为更好地进行测试做好准备。请参考1.1.4节介绍的W软件测试模型。

软件测试工作的分配比例应该与预期的和后期观察到的缺陷分布模块相适应。少数模块通常包含大部分在软件测试版本中发现的缺陷或失效。这个符合80-20原则,即80%的缺陷发生在20%的模块中。造成这种现象的可能性如下:

James Whittaker等著的《探索式软件测试》书中提到对软件灾难区进行重点测试也是基于这点考虑的。

案例1-18:缺陷集群性。

产品项目同案例1-17,根据前两周的测试,总结出来的缺陷报告如下:

A

B

C

D

E

F

G

1

0

0

2

0

0

0

2

3

2

6

1

4

2

6

4

4

14

3

1

2

合计

17

13

10

42

6

13

8

图像说明文字 注:合计=高级别个数×5+中级别个数×3+低级别个数×1

由此可见,模块D的缺陷是最多的,其次为模块A,然后是模块B,模块F和模块C,模块E和模块G相对缺陷比较少。根据缺陷集群性,测试经理调整第三周的测试任务如下:

A、D、C、E

D、B、F

D、B、F、G

A、F、C

采用同样的测试用例多次重复进行测试,最后将不再发现新的缺陷。为了克服这种“杀虫剂悖论”,测试用例需要进行定期评审和修改,同时需要不断增加新的不同的测试用例,来测试软件或系统的不同部分,从而发现更多的潜在缺陷。具体可以参见1.1.11节中关于杀虫剂现象的描述。

针对不同的软件测试背景,进行不同的软件测试活动。比如,对通信系统的软件进行软件测试,与对嵌入式机顶盒系统软件的软件测试的方法是不一样的。

假如系统无法使用,或者系统不能完成客户的需求和期望,发现和修改缺陷是没有任何意义的。

(1)“验证(Verification)”的含义是通过提供客观证据对规定要求已得到满足的认定,它要查明工作产品或方法是否恰当地反映了规定的要求。验证要保证“Do thing right”。也就是说,“验证要用数据证明我们是不是在正确地制造产品”。

(2)“确认(Validation)”的含义是通过提供客观证据对特定的预期用途或应用要求已得到满足的认定,它要证明所提供的(或将要提供的)产品或方法适合其预计的用途。确认要保证“Do right thing”。也就是说,“确认就是要用数据证明我们是不是制造了正确的产品”。

(3)“验证”和“确认”之间的区别和联系:“验证”和“确认”都是认定。但是,“验证”表明的是满足规定要求,而“确认”表明的是满足预期用途或应用要求。简单说,“确认”就是检查最终产品是否达到用户使用要求。

ISO 9000对确认和验证的定义分别如下。

下面通过图1-21和图1-22,加深对这两个概念的理解。

图1-21 验证与确认区别(一)

图1-22 验证与确认区别(二)

案例

所在章节

案例1-1:函数级别黑盒测试

1.1.2-4黑盒测试(Black Box Testing)

案例1-2:单元测试

1.1.2-5单元测试(Unit Testing)

案例1-3:集成测试

1.1.2-6集成测试(Integration Testing)

案例1-4:测试用例的书写

1.1.8-2测试用例案例

案例1-5:功能测试

1.1.9-1功能测试

案例1-6:易用性测试

1.1.9-2易用性测试(用户体验性测试)

案例1-7:电信系统软件的可靠性

1.1.9-3可靠性测试

案例1-8:Web系统的性能测试

1.1.9-4性能测试

案例1-9:黑客侵入

1.1.9-5安全性测试

案例1-10:XSS注入

1.1.9-5安全性测试

案例1-11:SQL注入

1.1.9-5安全性测试

案例1-12:设备接口兼容性

1.1.9-6兼容性测试

案例1-13:屏幕分辨率测试

1.1.9-6兼容性测试

案例1-14:网络设备移植测试

1.1.9-7可移植性测试

案例1-15:代码可维护性测试

1.1.9-8可维护性测试

案例1-16:产品的可测试性

1.1.9-8可维护性测试

案例1-17:根据软件杀虫剂现象进行测试计划调整

1.1.11软件的杀虫剂现象

案例1-18:缺陷集群性

1.2.4原则4:缺陷集群性


从本章开始介绍软件测试的各种设计方法,这是学习软件测试的重要内容。

一般来说,软件测试设计方法分为5类:传统的黑盒测试方法、基于质量的测试方法、基于风险的测试方法、基于经验的测试方法以及白盒测试方法。本章主要介绍传统的黑盒测试方法和白盒测试方法,共分7节。

基于质量的测试方法请参看1.1.9软件测试类型,本书中量的测试方法参看第1.1.9节软件测试类型,本书不再进行深入的介绍行深入介绍;基于风险的测试方法将在第四章进行介绍在第4章介绍;基于经验的测试方法将在第三章进行介绍。另外关于软件测试在第3章介绍。另外,关于软件测试的设计,读者可以参考参考文献【5】、【13】进行深入的学习行深入学习。

通过等价类/边界值法设计软件测试用例是测试用例设计的最基本的方法。这两种方法密不可分。下面先介绍“等价类分析法”。

等价类是指软件测试对象的某个参数输入域的子集合。在该子集合中,各个输入数据对于识别软件测试对象中的缺陷是等价的。只要测试等价类的某一个代表值,就可以认为覆盖了该等价类所有其他值的软件测试。

等价类的划分

是把软件测试对象的输入域划分成若干部分,然后从每一部分中选取少数具有代表性的数据,作为测试用例输入数据的测试用例设计技术。

等价类的两个假设

(1)软件测试对象等价类中任意一个代表值没有发现缺陷,则认为等价类内其他值也不能发现缺陷。比如等价类为【0~5】,如果测试数据4没有发现缺陷,那么测试数据1也不可能发现缺陷。

(2)软件测试对象等价类中任意一个代表值可以发现缺陷,则认为等价类内其他值也都可以发现缺陷。比如等价类为【0~5】,如果测试数据4发现缺陷,那么测试数据1也肯定能发现缺陷。

有效等价类与无效等价类

(1)有效等价类:对于软件测试对象而言,有效等价类指的是合理的、有意义的数据构成的集合。

(2)无效等价类:对于软件测试对象而言,无效等价类指的是不合理的、没有意义的数据构成的集合。

案例2-1:等价类的分类。

通过表2-1来看各种类型的数据是如何通过等价类进行分类的。

表2-1 等价类的设计法

需求 有效等价类 无效等价类
连续的数字 【20.0~30.0】 (≥20.0)、(≤30.0) (<20.0)、(>30.0)
离散的数字 【20~30】的整数 【20~30】的整数 (<20)、(>30)、【20~30】的浮点数
有序的集合 【0-10岁】
【11-20岁】
【21-40岁】
【41-60岁】
【0-10岁】
【11-20岁】
【21-40岁】
【41-60岁】
整数 16 bit int 【32767,-32767】 >32767、<-32767
屏幕光标 【0~1204】×【0~768】 【0~1204】×【0~768】 ≥1205×≥769、<0×<0
遵守规则 Email地址 a@b.com a@b..com)、(a@c@b.com)、(a@)、(b)…

下面再介绍一下“边界值分析法”。

边界值分析是对输入或输出的边界值进行软件测试的一种测试方法。通常,边界值分析法作为对等价类划分法技术的补充。这种情况下,其测试用例来自等价类的边界。由于程序员在开发时在边界区域比较容易犯错误(如原本应该为a<100,却写成a≤100),所以边界值测试法就显得非常重要。由于边界值是随着等价类出现的,所以边界值可以分为有效等价类的边界值和无效等价类的边界值。

边界值分析的步骤如下:

(1)识别软件测试对象中的参数等价类;

(2)识别每个等价类的边界值;

(3)创建边界值的相关测试用例;

(4)定义边界值分析技术的覆盖率。

案例2-2:边界值的设计法。

基于表2-1,来看各种情况的边界值如何划定,见表2-2。

表2-2 边界值的设计法

需求 有效等价类边界值 无效等价类边界值
连续的数字 【20.0~30.0】 (20.0)、(30.0) (19.999)、(31.001)
离散的数字 【20~30】的整数 (20)、(30) (19)、(31)
有序的集合 【0-10岁】
【11-20岁】
【21-40岁】
【41-60岁】
【0-10岁】
【41-60岁】
整数 16 bit int 32767、-32767 32768、-32768
屏幕光标 1204×768 (1204×768)、(0×0) (1205×769)

等价类/边界值除了可以以输入进行分类,也可以以输出进行分类,如案例2-3所示。

案例2-3:公园门票规定:

我们可以划分等价类为全票、半票和免票,见表2-3。

表2-3 公园门票等价类/边界值

编号 输出 输入 边界值
1 免票 身高1.2m以下儿童 身高1.19m/1.2m儿童
年龄在70岁以上的老人 年龄69岁/70岁的老人
革命烈士家属 革命烈士家属
在职军人 在职军人
2 半票 身高1.2~1.4m的儿童 身高1.2m/1.21m儿童
年龄在60~69岁之间的老人 年龄59岁/60岁/69岁/70岁的老人
在校学生(不含在职学生,电大学生) 在校学生
3 全票 身高1.2m以上儿童 身高1.21m的儿童
年龄在70岁以下的老人 年龄在49岁的老人
在职学生,电大学生 在职学生,电大学生

如果系统中有多处需要使用等价类设计的测试用例,对于有效等价数据类,可以在一个测试用例中使用;而对于无效等价类数据,在一个测试用例中只能出现一个。

案例2-4:等价类测试。

图2-1是用户信息输入的部分界面,针对这个界面设计测试用例。

图2-1 用户信息输入的部分界面

 

这里性别也需要输入,只允许输入“男”和“女”。

对于有效等价数据类,设计测试用例如下。

(1)姓名:小明,年龄:0岁,性别:男。

(2)姓名:阿拉克拉姆,年龄:130岁,性别:女。

而对于无效等价类,如果设计的测试用例如下。

姓名:明,年龄:200岁,性别:男。

那么系统如果只报告了姓名有误,而没有报告年龄有误的信息,就无法知道年龄是否在程序中进行了有效性检验。也就是说,出现“缺陷屏蔽”,所以,对于无效等价类,测试用例应该细化如下。

(1)姓名:克,年龄:13,性别:男。

(2)姓名:阿拉克拉姆萨,年龄:13,性别:男。

(3)姓名:@ @,年龄:13,性别:男。

(4)姓名:小明,年龄:134,性别:男。

(5)姓名:小明,年龄:13,性别:中。

这样就可以看出:有效等价类的总个数为每个用例有效等价类个数的笛卡儿积(1×1×2=2);而无效等价类的总个数为每个用例有效等价类个数的和(1+1+3=5)。

另外,如果只要求测试等价类,而对边界值测试要求不高,在测试用例的设计中尽可能多地用到边界值。

案例2-5:由于边界值测试不完善带来的Bug。

这是我亲自遇到的一个案例。有一天我去上海某医院看病,由于手头现金没有带够,需要在医院的门口一台ATM机上取款,取款机上有一个提示,“一次取款不得多于¥2000,每天最多取5次”,于是我用我的借记卡准备取¥2000,系统却告诉我“已经超过一次取款的最大金额”,我感到很纳闷,于是改为¥1900,取款成功;作为测试工程师,我马上就意识到该系统中的边界值测试没有做好或者根本没有进行边界值的测试。

案例2-6:日历等价类/边界值测试。

最后以一个案例作为本节的结束,如图2-2所示。

图2-2 出生年月日的等价类/边界值测试用例设计

等价类/边界值测试法是最基本的测试用例设计方法,不管是函数级别的软件测试,还是系统级别的软件测试都可以使用。

决策表方法是一种很好的方法,它可以识别含有逻辑条件的系统需求,还可以将内部系统设计文档化。这种方法可以用来记录一个系统要实施的复杂的业务规则。建立决策表时,要分析规格说明,并识别系统的条件和动作。输入条件和动作通常以“真”或“假”(布尔变量)的方式表述。决策表包含了触发条件,通常还有各种输入条件“真”或“假”的组合以及各条件组合相应的输出动作。决策表的每一列对应了一个业务规则,该规则定义了各种条件的一个特定组合,以及这个规则相关联的执行动作。决策表测试的常见覆盖标准是每列至少对应一个测试用例,该测试用例通常覆盖触发条件的所有组合。

决策表测试的优点是可以生成测试条件的各种组合,而这些组合利用其他方法可能无法被测试到。它适用于当软件的行为由一些逻辑决策决定的情况。

案例2-7:四边形类型判断系统。

下面来看一个例子。a、b、c、d是四边形的4条边,通过平行关系与是否相等来判断四边形的类型,四边形如图2-3所示。

图2-3 四边形

a、b、c、d为四边形的4条边,可以获得如下条件。

C1:a//c(C1=T表示a平行于c;C1=F表示a不平行于c)。

C2:b//d(C2=T表示b平行于d;C2=F表示b不平行于d)。

C3:a的长度与b是否相等。

C4:b的长度与d是否相等。

四边形类型有。

A1:平行四边形。

A2:非等腰梯形。

A3:等腰梯形。

A4:普通四边形。

A5:不存在。

根据如上描述,做出决策表(由于条件有4个,所以一共有24=16个组合),见表2-4。

第一列:a不平行于c,b不平行于d,a不等于c,b不等于d,判定为普通四边形。

第二列:a不平行于c,b不平行于d,a不等于c,b等于d,判定为普通四边形。

第三列:a不平行于c,b不平行于d,a等于c,b不等于d,判定为普通四边形。

第四列:a不平行于c,b不平行于d,a等于c,b等于d,判定这种四边形不存在。

第五列:a不平行于c,b平行于d,a不等于c,b等于d,判定这种四边形为非等腰梯形。

以此类推,可以得到共十六列结果,见表2-4。

表2-4 四边形类型判断系统决策表设计(调整前)

条件12345678910111213141516
C1:a//cFFFFFFFFTTTTTTTT
C2:b//dFFFFTTTTFFFFTTTT
C3:a=cFFTTFFTTFFTTFFTT
C4:b=dFTFTFTFTFTFTFTFT
动作
A1:平行四边形               X 
A2:非等腰梯形    X   X      
A3:等腰梯形      X  X      
A4:普通四边形XXX             
A5:不存在   X X X  XXXXX 

根据表2-4,下面来做一些简化。

根据列1和2,只要C1=F、C2=F、C3=F,就可以判断为A4。

根据列6和8,只要C1=F、C2=T、C4=T,就可以判断为A5。

根据列4和12,只要C2=F、C3=T、C4=T,就可以判断为A5。

根据列11和15,只要C1=T、C3=T、C4=F,就可以判断为A5。

根据列13和14,只要C1=T、C2=T、C3=F,就可以判断为A5。

经过简化后,得到表2-5。

表2-5 四边形类型判断系统决策表设计(调整后)

条件

1

2

3

4

5

6

7

8

9

10

11

12

C1:a//c

F

F

-

F

F

F

T

T

T

T

T

T

C2:b//d

F

F

F

T

T

T

F

F

-

T

T

T

C3:a=c

F

T

T

F

-

T

F

F

T

F

T

T

C4:b=d

-

F

T

F

T

F

F

T

F

-

F

T

动作

A1:平行四边形

X

A2:非等腰梯形

X

X

A3:等腰梯形

X

X

A4:普通四边形

X

X

A5:不存在

X

X

X

X

X

这样,16个测试用例就被简化成12个,于是测试用例可以设计成表2-6。

表2-6 四边形类型测试用例

编号

a的长度

b的长度

c的长度

d的长度

a//c

b//d

结果

1

2

3

1

4

F

F

普通四边形

2

2

3

2

4

F

F

普通四边形

3

2

2

2

2

F

F

不存在

4

2

3

4

5

F

T

非等腰梯形

5

2

3

3

3

F

T

不存在

6

3

2

3

4

F

T

等腰梯形

7

2

3

4

5

T

F

非等腰梯形

8

2

4

3

4

T

F

等腰梯形

9

4

3

4

5

T

F

不存在

10

2

3

4

5

T

T

不存在

11

2

3

2

4

T

T

不存在

12

2

3

2

3

T

T

平行四边形

案例2-8:用户登录系统。

下面再来看一下用户登录系统的测试用例应该如何设计。同样,也可以用决策表的方法。用户登录系统一般包括用户名、密码和验证码。只要用户名、密码错误,系统就会报出错信息:“用户名或者密码错误”;如果用户名、密码都正确,验证码错误,系统报出错信息:“验证码错误”;用户名、密码和验证码都正确,正常进入系统。下面就可以考虑如何用决策表来设计测试用例。

条件如下。

动作如下。

如上所述,可以做出登录系统的决策表设计(系统有3个条件,所以有23=8个组合),见表2-7。

表2-7 登录系统决策表设计(调整前)

条件

1

2

3

4

5

6

7

8

C1:正确的用户名

F

F

F

F

T

T

T

T

C2:正确的密码

F

F

T

T

F

F

T

T

C3:正确的验证码

F

T

F

T

F

T

F

T

动作

A1:用户名或密码错误

X

X

X

X

X

X

A2:验证码错误

X

A3:进入系统

X

 第一列:用户名错误,密码错误,验证码错误,得到提示信息“用户名或密码错误”。

 第二列:用户名错误,密码错误,验证码正确,得到提示信息“用户名或密码错误”。

 第三列:用户名错误,密码正确,验证码错误,得到提示信息“用户名或密码错误”。

 第四列:用户名错误,密码正确,验证码正确,得到提示信息“用户名或密码错误”。

 第五列:用户名正确,密码错误,验证码错误,得到提示信息“用户名或密码错误”。

 第六列:用户名正确,密码错误,验证码正确,得到提示信息“用户名或密码错误”。

 第七列:用户名正确,密码正确,验证码错误,得到提示信息“验证码错误”。

 第八列:用户名正确,密码正确,验证码正确,进入系统。

根据表2-7,下面来做些简化。

(1)根据列1、2、3、4,只要C1=F,就可以执行动作A1。

(2)根据列1、2、5、6,只要C2=F,就可以执行动作A1。

简化后,得到表2-8。

表2-8 登录系统决策表设计(调整后)

条件

1

2

3

4

C1:正确的用户名

F

-

T

T

C2:正确的密码

-

F

T

T

C3:正确的验证码

-

-

F

T

动作

A1:用户名或密码错误

X

X

A2:验证码错误

X

A3:进入系统

X

这样,8个测试用例就简化成4个。于是,测试用例可以这样设计:假设用户名:Kenny,密码:khnygh,验证码:243546。登录系统测试用例见表2-9。

表2-9 登录系统测试用例

编号

用户名

密码

验证码

期待结果

1

Tom

khnygh

243546

提示:用户名或密码错误

2

Kenny

oooo

243546

提示:用户名或密码错误

3

Kenny

khnygh

12345

提示:验证码错误

4

Kenny

khnygh

243546

进入系统

案例2-9:飞机票定价系统。

下面是一个飞机票定价系统的例子,需求如下。

(1)乘客可以免费托运重量不超过30kg(含30kg)的行李。

(2)假如行李超过30kg,其收费标准为。

 头等舱国内乘客:超重部分每千克收费4元。

 其他舱国内乘客:超重部分每千克收费6元。

 外国乘客:超重部分每千克比国内乘客多1倍。

 残疾乘客:为正常价格的半价。

(3)行李重量超出部分,不满1kg的按照1kg计算。

经过分析,条件如下。

 C1:国内乘客。

 C2:超重游客。

 C3:头等舱乘客。

 C4:残疾乘客。

金额

 A1:免费。

 A2:2元。

 A3:3元。

 A4:4元。

 A5:6元。

 A6:8元。

 A7:12元。

根据如上描述做出决策表(由于条件有4个,所以一共有24=16个组合)。

 第一列:携带行李不超过30kg,普通舱,非残疾国外乘客:免费。

 第二列:携带行李不超过30kg,普通舱,残疾国外乘客:免费。

 第三列:携带行李不超过30kg,头等舱,非残疾国外乘客:免费。

 第四列:携带行李不超过30kg,头等舱,残疾国外乘客:免费。

 第五列:携带行李超过30kg,普通舱,非残疾国外乘客:8元。

 第六列:携带行李超过30kg,普通舱,残疾国外乘客:4元。

 第七列:携带行李超过30kg,头等舱,非残疾国外乘客:12元。

 第八列:携带行李超过30kg,头等舱,残疾国外乘客:6元。

以此类推,可以得到所有16列的结果,见表2-10。

表2-10  飞机票定价系统决策表设计(调整前)

条件

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

C1:国内乘客

F

F

F

F

F

F

F

F

T

T

T

T

T

T

T

T

C2:超重乘客

F

F

F

F

T

T

T

T

F

F

F

F

T

T

T

T

C3:头等舱乘客

F

F

T

T

F

F

T

T

F

F

T

T

F

F

T

T

C4:残疾乘客

F

T

F

T

F

T

F

T

F

T

F

T

F

T

F

T

动作

A1:免费

X

X

X

X

X

X

X

X

A2:2元

X

A3:3元

X

A4:4元

X

X

A5:6元

X

X

A6:8元

X

A7:12元

X

根据表2-10,简化如下。

(1)根据列1、2,只要C1=F、C2=F、C3=F,就可以执行动作A1。

(2)根据列3、4,只要C1=F、C2=F、C3=T,就可以执行动作A1。

(3)根据列9、10,只要C1=T、C2=F、C3=F,就可以执行动作A1。

(4)根据列11、12,只要C1=T、C2=F、C3=T,就可以执行动作A1。

于是得到表2-11。

表2-11  飞机票定价系统决策表设计(调整中)

条件

1

2

3

4

5

6

7

8

9

10

11

12

C1:国内乘客

F

F

F

F

F

F

T

T

T

T

T

T

C2:超重乘客

F

F

T

T

T

T

F

F

T

T

T

T

C3:头等舱乘客

F

T

F

F

T

T

F

T

F

F

T

T

C4:残疾乘客

-

-

F

T

F

T

-

-

F

T

F

T

动作

A1:免费

X

X

X

X

A2:2元

X

A3:3元

X

A4:4元

X

X

A5:6元

X

X

A6:8元

X

A7:12元

X

根据表2-11,还可以进一步调整。

根据列1、2、7、8,只要C2=F,就可以执行动作A1,见表2-12。

表2-12  飞机票定价系统决策表设计(调整后)

条件

1

2

3

4

5

6

7

8

9

C1:国内乘客

-

F

F

F

F

T

T

T

T

C2:超重乘客

F

T

T

T

T

T

T

T

T

C3:头等舱乘客

-

F

F

T

T

F

F

T

T

C4:残疾乘客

-

F

T

F

T

F

T

F

T

动作

A1:免费

X

A2:2元

X

A3:3元

X

A4:4元

X

X

A5:6元

X

X

A6:8元

X

A7:12元

X

最后,16个测试用例就简化为9个测试用例了。可以设计表2-13所示的测试用例。

表2-13  飞机票定价系统测试用例

编号

旅客类型

收费

1

非携物超重行李旅客

免费

2

携物超重行李的国外非头等舱旅客

8元

3

携物超重行李的国外非头等舱残疾旅客

4元

4

携物超重行李的国外头等舱旅客

12元

5

携物超重行李的国外头等舱残疾旅客

6元

6

携物超重行李的国内非头等舱旅客

4元

7

携物超重行李的国内非头等舱残疾旅客

2元

8

携物超重行李的国内头等舱旅客

6元

9

携物超重行李的国内头等舱残疾旅客

3元

基于状态转换软件测试设计是软件测试设计的另一种方法,这种方法具有以下4个特征。

(1)软件测试对象的输出和行为方式不仅受当前输入数据的影响,同时还与软件测试对象之前的执行情况、之前的事件或以前的输入数据等有关。

(2)通过引入状态图(State Diagram)来描述软件测试对象和软件测试数据、对象状态之间的关系。

(3)状态图中的各个状态是通过不同的事件驱动的,如函数的调用。

(4)基于状态图开展的测试称之为状态转换测试。

状态图转化法最早运用于嵌入式测试用例设计。在嵌入式软件中,系统通过某种行为驱动能够从一种状态改变到另一种状态。图2-4是内存状态转换图。

图2-4 内存状态转换图

许多书中都以图2-4作为案例,进行状态转换法测试用例设计的介绍。但是,笔者认为这个图比较麻烦,不利于初学者掌握。下面以视频播放软件作为案例,来给大家介绍一个比较简单易懂的状态转化图,如图2-5所示。

图2-5 视频播放软件

案例2-10:视频播放机。

这个软件的功能是:打开视频播放机,系统处于“开机”状态,单击【运行】键,系统处于“运行”状态;单击【停机】键,播放结束,系统处于停机状态;在“运行”状态单击【快进】键,进入“快进”状态,【快进】键最多可以按4次,分别为2倍数、4倍数、8倍数和16倍数前进;快进状态单击【停止】键返回“运行”状态,停机状态单击【播放】键,重新进入“运行”状态。

状态转换图转为状态转换树的方法是:

(1)状态树的节点描述状态图的状态,状态树的枝干描述状态图的事件。

(2)转换树的根节点为状态图的初始状态,转换树的终节点为叶节点。

(3)转换树的每个节点,在状态图中如有直接后续状态,则添加一个枝干和节点(不同的事件应有不同的枝干和节点),直到出现如下情况,可将此节点作为叶节点:

 或者:

来源:“Testing Software Design Modeled by Finite-State Machines”, IEEE Transactions on Software engineering, vol.4, no 3, may 1978, p 178-187

状态转换软件测试覆盖率:

(1)覆盖软件测试对象所有的状态;

(2)覆盖软件测试对象所有的事件;

(3)覆盖软件测试对象所有的状态转换至少一次;

(4)覆盖软件测试对象所有的状态、事件和状态转换。

为了能够得到比较高的状态转换软件测试覆盖率,再把状态图转换成状态树,然后再设计测试用例。下面讨论视频播放软件状态图是如何转换成状态树的。

图2-6为该软件的0-switch转换图。有了这棵树,就可以设计测试用例了。从树的根节点到所有叶子节点就是一个测试用例,这样就得到4个测试用例,分别为:

图2-6 视频播放软件状态转换图(0-switch)

(1)开机->运行->快进->运行;

(2)开机->运行->快进->快进;

(3)开机->运行->停机->运行;

(4)开机->运行->停机。

上面这棵树叫作0-switch展开,也就是最基本的展开法。为了得到更多的测试用例,可以把这棵树的非结束的叶子节点再进行一次展开,也就是1-switch展开,如图2-7所示。

这样,可以得到7个测试用例:

(1)开机->运行->快进->运行->快进;

(2)开机->运行->快进->运行->停机;

(3)开机->运行->快进->快进->运行;

(4)开机->运行->快进->快进->快进;

图2-7 视频播放软件状态转换图(1-switch)

(5)开机>运行->停机;

(6)开机>运行->停机->运行->停机;

(7)开机>运行->停机->运行->快进。

按照这种方法可以设计2-switch、3-switch……但是,在实际工作中,没有特殊情况,做到1-switch就已经足够了。

状态转换图也可以转换为状态转换表,方法是:表头和第一列依次对应各个节点,如果这个节点与所对应的节点有链接,在表中值为边值,否则值为X。视频播放软件状态转换表见表2-14。

表2-14  视频播放软件状态转换表

开机

运行

快进

停机

开机

X

X

X

X

运行

a

X

d

X

快进

X

b

c

X

停机

X

e

X

X

对于表2-14,可以设计进行破环性测试用例,主要针对表中的X地方,如:快进到停机为X,这样可以设计一个测试用例,当播放软件处于快进阶段,强制按停止键,看系统会发生什么反应,说不定这里就有一个Bug(对于用户友好性测试的建议,当系统处于播放时,【运行】按键是虚的;当系统处于快进时,【停止】按键是虚的;当系统处于停止时,【快进】按键是虚的。)

笔者在软件测试实践中,发现状态转换法也可以运用在流程控制系统测试用例设计中,以设计出对每一个环节进行有效遍历的测试用例。

案例2-11:电子商务购物。

图2-8是电子商务购物的业务流程状态转化图。

图2-8 电子商务购物的业务流程状态转化图

由于图2-8很复杂,所以只需对关键部分进行0-switch展开就够了,如图2-9所示。

图2-9 电子商务购物的业务流程状态转化树

这样设计出以下5个测试用例:

(1)登录->首页->查看商品->查看商品详情->下单->付款;

(2)登录->首页->修改用户信息->返回首页->查看商品->查看商品详情->查看其他商品详情->下单->付款;

(3)登录->首页->查看商品详情->下单->付款;

(4)登录->首页->查看商品详情->查看购物车->下单->付款;

(5)登录->首页->重置密码->返回首页->查看商品详情->查看商品->查看购物车->下单->付款。

 

这里默认查看商品详情就是把这个商品放入了购物车。

对于业务流程,设计到0-switch就可以了。

条件组合是软件测试设计中普遍遇到,而且又是十分头痛的事情,这里介绍两个经常用到的方法:决策树和正交法。决策树不太正规,而正交法基于严格的数学理论,所以比较正规,但是掌握起来有一定难度。本节介绍决策树,下一节介绍正交法。

案例2-12:文本编辑软件。

这是一个简化版的文本编辑器软件,仅可以编辑文字的格式和字号,格式分为黑体、斜体和下划线3种;字号包括1、2、3、4、5共5个字号。图2-10就是用决策树设计的测试用例方法:

图2-10 编辑器的决策树

 对于3种字体,依次循环取:黑体、斜体、下划线、黑体、斜体、下划线……

 对于5种字号,依次循环取:1号、2号、3号、4号、5号、1号、2号、3号……

这样,每一行就对应一个测试用例,即:

(1)黑体1号字体;

(2)斜体2号字体;

(3)下划线3号字体;

(4)黑体4号字体;

(5)斜体5号字体;

(6)下划线1号字体;

(7)黑体2号字体;

……

至于需要取多少行,这由测试能力决定。

案例2-13:机票购买系统。

下面来看一个机票购买系统的例子,界面如图2-11所示。

图2-11 机票购买系统

为了简化起见,仅假设下面几种情形:

 第一项包括往返、单程和联票。

 第二项与第三项出发,到达城市仅为上海、北京、福州、杭州、济南。

 第四项出发日期仅为 2015-06-15、2015-06-16、2015-06-17。

 第五项返回日期仅为 2015-06-18、2015-06-19、2015-06-20。

机票购买系统决策树如图2-12所示。

图2-12 机票购买系统决策树

这里,出发城市与到达城市均为5个,并且相同,如果按照上面介绍的思路,测试用例永远是上海—上海、北京—北京、福州—福州、杭州—杭州、济南—济南。改变一下策略,出发城市依次循环为上海、北京、福州、杭州、济南;到达城市从北京开始,依次为北京、福州、杭州、济南、上海;下一次从福州开始,依次为福州、杭州、济南、上海、北京;再下一次从杭州开始依次为杭州、济南、上海、北京、福州……依次类推。

对于出发时间与返回时间也做同样处理。出发时间仍旧为2015-06-15、2015-06-16、2015-06-17的循环,返回时间第一个循环以2015-06-18开始:2015-06-18、2015-06-19、2015-06-20;第二个循环以2015-06-19开始:2015-06-19、2015-06-20、2015-06-18;第三个循环以2015-06-20开始:2015-06-20、2015-06-18、2015-06-19的循环。

这里还要注意以下几个方面。

根据上面的设计,得到以下测试用例。

(1)往返,上海到北京,出发时间:2015-06-15,返回时间:2015-06-18:获得相应的查询结果,先让用户选择出发时间的航班,然后选择返回时间的航班。

(2)单程,北京到福州,出发时间:2015-06-16,返回时间:不允许选择:正常获得相应的查询结果。

(3)联票:福州到杭州,出发时间:2015-06-17,返回时间:不允许选择:正常获得相应的查询结果。

……

案例2-14:网站兼容性组合测试。

本节介绍采用正交法设计多种组合情况下的测试用例。

下面是软件测试某网站系统的例子。

(1)8种浏览器:IE9、IE10、IE11、Netscape 9.0、火狐36、Safari 5.3、Firefox 35、百度7.2。

(2)3种Office插件:Office 2007、Office 2010、Office 2013。

(3)6种客户端操作系统:Windows XP、Windows Vista、Windows 8.0、Windows 8.1、Windows 9、Windows 9.1。

(4)3种服务器软件:IIS、Apache、WebLogic。

(5)3种服务器端操作系统:Windows 2003、Windows 2008、Linux。

如果达到100%组合的软件测试,需要设计8×3×6×3×3=1296个测试用例,这个数据非常庞大,利用正交法可以在最大程度上减少测试用例,而且可以保证软件测试一定的可靠性。下面简单介绍一下正交法。

正交法又称两两组合法,它保证测试用例中的两两组合不同,但两两覆盖所有的组合。比如,现在有3个变量,每个变量有两种取值,标记为0和1。这样,测试用例为2×2×2=8个,这种情况下的正交表见表2-15。

表2-15  L4(23) 正交表

行号

A

B

C

1

0

0

1

2

1

0

0

3

0

1

0

4

1

1

1

 第1、3、2、4行,AB分别对应00、01、10、11(满足了0和1的所有4种组合)。

 第2、1、3、4行,BC分别对应00、01、10、11(满足了0和1的所有4种组合)。

 第3、1、2、4行,AC分别对应00、01、10、11(满足了0和1的所有4种组合)。

这样,8个测试用例就被简化为4个,减少率为50%。这种情况标记为:

L4(23)

这里,2代表有2个取值,3代表有3个变量,4代表有4种组合。

下面介绍本节开始的那个网站如何使用正交法来设计测试用例。

(1)8种浏览器:IE9、IE10、IE11、Netscape 9.0、火狐36、Safari 5.3、Firefox 35、百度7.2。

(2)3种Office插件:Office 2007、Office 2010、Office 2013。

(3)6种客户端操作系统:Windows XP、Windows Vista、Windows 8.0、Windows 8.1、Windows 9.0、Windows 9.1。

(4)3种服务器软件:IIS、Apache、WebLogic。

(5)3种服务器端操作系统:Windows 2003、Windows 2008、Linux。

8个取值有1个,6个取值有1个,3个取值有3个,所以需要找到是否存在816133正交表,这样的正交表不存在,但是在正交表中找到了最接近的L64(8243)表,为此需要对原来的取值进行调整,加入No used选项,调整后的结果如下。

(1)8种浏览器:IE9、IE10、IE11、Netscape 9.0、火狐36、Safari 5.3、Firefox 35、百度7.2。

(2)3种Office插件:Office 2007、Office 2010、Office 2013、No used。

(3)6种客户端操作系统:Windows XP、Windows Vista、Windows 8.0、Windows 8.1、Windows 9.0、Windows 9.1、No used、No used。

(4)3种服务器软件:IIS、Apache、WebLogic、No used。

(5)3种服务器端操作系统:Windows 2003、Windows 2008、Linux、No used。

L64(8243)的正交表如图2-13所示。

图2-13 L64(8243)正交表

用相应的值代替表中的数字,如表2-16所示。

表2-16 网站兼容性测试与正交表替换

编号

浏览器

Office插件

客户端操作系统

服务器软件

服务器端操作系统

1

IE 9

Office 2007

Windows XP

IIS

Windows 2003

2

IE 10

Office 2010

Windows Vista

Apache

Windows 2008

3

IE 11

Office 2013

Windows 8.0

WebLogic

Linux

4

Netscape 9.0

No used

Windows 8.1

No used

No used

5

火狐36

Windows 9.0

6

Safari 5.3

Windows 9.1

7

Firefox 35

No used

8

百度7.2

No used

把所有的值都代入L64(8243)正交表,得到如表2-17所示。

表2-17 得到的测试用例

编号

浏览器

Office插件

客户端操作系统

服务器软件

服务器端操作系统

1

IE 9

Office 2007

Windows XP

No used

Windows 2003

2

IE 9

No used

Windows 8.0

No used

No used

3

IE 9

No used

Windows Vista

IIS

No used

4

IE 9

Office 2007

Windows 8.1

WebLogic

Windows 2003

5

IE 9

Office 2013

Windows 9.0

Apache

Linux

6

IE 9

Office 2010

Not used

Apache

Windows 2008

7

IE 9

Office 2010

Windows 9.1

WebLogic

Windows 2008

8

IE 9

Office 2013

Not used

WebLogic

Linux

9

IE 10

No used

Windows XP

Apache

Linux

10

IE 10

Office 2007

Windows 8.0

Apache

Windows 2008

这样,1296个测试用例就被简化为64个,减少率为95%。关于正交表如何获得,是有许多工具可以产生的,读者可以在网上搜索。

上面介绍的5个测试用例的设计方法大部分都适用于黑盒测试。下面让我们来详细介绍软件白盒测试的一些知识。先来看一下由Main Cohn提出的著名的软件测试金字塔,如图2-14所示。

图2-14 软件测试金字塔

由于白盒测试是单元测试的主要内容,所以白盒测试在整个软件测试过程中很重要。

白盒测试覆盖包括语句覆盖、分支覆盖、条件覆盖、分支/条件覆盖、MC/DC(修订的条件/判定)覆盖、路径覆盖、控制流覆盖等,这些是白盒测试技术中基本的概念,将在本节中详细介绍。在介绍之前,看一下本节中将要用到的一个程序,如图2-15所示。

图2-15 白盒测试的例子

这里包括如下。

 3个语句:S1、S2、S3。

 2个判断:M1、M2。

 4条路径:L1:ace、L2:abe、L3:acd、L4:abd。

 8个条件:T1:x>3、T2:z<10、T3:x=4、T4:y>5、F1:x<=3、F2:j>=10、F3:x<>4、F4:y>=5。

语句覆盖(Statement Coverage)又叫行覆盖(Line Coverage)。段覆盖(Segment Coverage)。基本块覆盖(Basic Block Coverage),这是最常用、也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明、代码注释、空行等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{}也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只覆盖代码中的执行语句,不考虑各种分支的组合等。假如只要求达到语句覆盖,那么换来的测试效果的确不明显,很难发现代码中更多的问题。

语句覆盖率的公式可以表示如下。

语句覆盖率=被评价到的语句数量/可执行的语句总数×100%。

下面让我们来看看各种情况下的语句覆盖。

在顺序语句中语句覆盖:在顺序语句中,语句覆盖率最简单,只要把顺序语句中的每个语句都覆盖到。

int f (int a,int b){
int c;
c=a+b;
return c;
}

语句覆盖率100%测试用例:f(1,2)。

在没有else的判断语句中语句覆盖:只要执行if语句中的内容就可以了。

int f (int a){
int b=0;
if (a>0){
   b=1;
   }
return b;
}

语句覆盖率100%测试用例:f(1)。

在有else的判断语句中语句覆盖:既要执行if语句,也要执行else中的语句。

int f (int a){
int b=0;
  if (a>0){
    b=1;
  } else{
    b=2;
  }
return b;
}

语句覆盖率100%测试用例:f(1)(执行了b=1语句)、f(0)(执行了b=2语句)两个。

在循环语句中语句覆盖:循环体内的语句必须有且有一次被运行。

int f (int a){
for (i=0;i<=a;i++)
     …
     printf(“hello”,s);
     …
}

语句覆盖率100%测试用例:f(0)。这里需要特别强调的是:测试用例在循环体内语句,必须有且有一次被运行,是因为循环体内的语句可能很长,如果让它执行2次,10次,甚至50次,100次或更多次,这样单元测试的时间会变得很长,而且意义不大。另外,单元测试要求一个测试用例最好在0.5s内能够执行完毕。

在多条件语句中语句覆盖:每一个分支必须执行一次。

int f (int a){ 
        switch (a) {
        case:1 { f1(); break;}
        case:2 { f2(); break;}
        case:3 { f3(); break;}
        case:4 { f4(); break;}
}

语句覆盖率100%测试用例:f(1)、f(2)、f(3)、f(4) 共4个。

看看在开始的例子中,设计哪些数据可以达到语句覆盖100%?

案例2-15:语句测试覆盖率。

假设令x=4、y=8、z=5。

语句覆盖测试用例见表2-18。

表2-18 语句覆盖测试用例

测试用例

输出

M1

M2

路径

x=4、y=8、z=5

k=31、j=0

True

True

L4

由此可见,只需要设计一个测试用例,就可以达到语句覆盖率100%。

语句覆盖毕竟是最简单的覆盖,即使达到语句覆盖100%,软件也会出现问题。

这里举一个不能再简单的例子,看下面的被测试代码。

int foo(int a, int b) 
{ 
return a / b; 
}

如果软件测试工程师编写如下软件测试用例。

测试用例:a = 10、b = 5。

软件测试工程师的测试结果会告诉你,代码覆盖率达到了100%,并且所有软件测试用例都通过了。然而,遗憾的是,语句覆盖率达到所谓的100%,但是却没有发现最简单的Bug。比如,当b=0时,会抛出一个除以零的异常。

简而言之,语句覆盖就是设计若干个测试用例,运行被测程序,使得每一个可执行语句至少执行一次。这里的“若干个”意味着使用测试用例越少越好。

分支覆盖又称判定覆盖,就是设计若干个测试用例,运行被测程序,使得程序中每个判定的取真分支和取假分支至少一次。

分支覆盖率的公式可以表示如下:

分支覆盖=被执行的分支数量/所有分支数量×100%

下面来看各种情况下的分支覆盖。

分支覆盖没有else的判断语句:既要执行if语句为True的情况,也要执行if语句为False的情况。

int f (int a){
int b=0;
if (a>0){
   b=1;
 }
return b;
}

分支覆盖率100%测试用例:f(1)、f(0),这是区别分支覆盖与语句覆盖需要特别注意的地方。

有else的判断语句:既要执行if语句,也要执行else中的语句。

int f (int a){
int b=0;
  if (a>0){
    b=1;
  } else{
    b=2;
    }
return b;
}

分支覆盖率100%测试用例:f(1)、f(0)。

在循环语句中:循环体内的语句必须有且有一次被运行。

int f (int a){
for (i=0;i<a;i++)
     printf(“hello”,s);
}

分支覆盖率100%测试用例:f(1)。

在多条件语句中:每个分支语句必须执行一次,另外还要涉及一种所有case没有覆盖到的情形。

int f (int a){
       switch (a) {
       case:1 { f1(); break;}
       case:2 { f2(); break;}
       case:3 { f3(); break;}
       case:4 { f4(); break;}
}

分支覆盖率100%测试用例:f(1)、f(2)、f(3)、f(4)、f(5) 5个。

下面来看在开始例子中,设计哪些数据可以达到分支覆盖的100%?

案例2-16:分支测试覆盖率。

令x=4、y=8、z=5。

  然后令x=2、y=5、z=11。

通过这两组数据,就可以达到该程序分支覆盖率测试100%,见表2-19。

表2-19 分支覆盖测试用例(一)

测试用例

输出

M1

M2

路径

x=4、y=8、z=5

k=31、j=0

True

True

L4

x=2、y=5、z=11

k=0、j=0

False

False

L1

也可以设计另一组测试用例来达到分支覆盖的100%,这里不详细描述,见表2-20。

表2-20 分支覆盖测试用例(二)

测试用例

输出

M1

M2

路径

x=13、y=2、z=5

k=25、j=2

True

False

L2

x=4、y=11、z=6

k=0、j=2

False

True

L3

分支覆盖的优缺点如下。

如本例中:x=4 || y>5,y>5写成y<5,即使判定覆盖测试用例达到100%,但是软件还是测试不出。

在软件设计过程中,一个判定往往由多个条件组成,判定覆盖仅考虑了判定的结果,而没有考虑每个条件的可能结果。

条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,判定中的每个条件的所有可能结果至少出现一次。

条件覆盖率的公式可以表示如下:

条件覆盖率=被执行的条件数量/所有条件数量×100%。

下面来看条件覆盖的例子:

int f (int a, int b){
int c=0;
if ((a>0) &&(b>0)){
   c=1;
}else{
   c=2
}return c;
}

表2-21为条件覆盖测试用例。

表2-21 条件覆盖测试用例

a>0

b>0

软件测试数据

T

T

a=1、b=1

T

F

a=1、b=0

F

T

a=0、b=1

F

F

a=0、b=0

有些时候条件覆盖是达不到100%的,请看下面的程序:

int f (int a){
int c=0;
if ((a>0) &&(a<5)){
   c=1;
}else{
   c=2
}return c;
}

表2-22 条件覆盖测试用例不一定达到100%

a>0

a<5

软件测试数据

T

T

4

T

F

6

F

T

-1

F

F

由表2-22可以看到,既要达到a≤10又要达到a≥5是不可能的。

下面再来看在开始例子中,设计哪些数据可以达到条件覆盖的100%?

案例2-17:条件测试覆盖率。

令x=4、y=2、z=11。

令x=2、y=6、z=6。

经过以上测试用例,T1、T2、T3、T4、F1、F2、F3、F4都被执行了一次,条件覆盖率达到100%,如表2-23所示。

表2-23 条件覆盖测试用例

测试用例

输出

原子条件

路径

x=4、y=2、z=11

k=0、j=0

T1、F2、T3、F4

L3

x=2、y=6、z=6

k=0、j=1

F1、T2、F3、T4

L3

可以看出,这里虽然条件覆盖达到100%,但是语句覆盖都没有达到100%,S1语句根本没有执行到。

于是,在日常工作中为了弥补分支覆盖的不足,结合条件覆盖的不充分,提出了判定/条件覆盖。

判定/条件覆盖:设计足够的测试用例,使得判断中每个条件的所有可能取值至少执行一次,同时每个判断的所有可能判断结果,即要求各个判断的所有可能的条件取值组合至少执行一次。

我们仍旧以开始的程序为例。

案例2-18:判定/条件测试覆盖率。

令x=4、y=8、z=5。

令x=2、y=5、z=11。

可以看到:

所以,这样既达到了分支覆盖率是100%的情形,也达到了条件覆盖率是100%的情形,见表2-24。

表2-24 判定/条件覆盖测试用例

测试用例

输出

原子条件

M1

M2

路径

x=4、y=8、z=5

k=31、j=0

T1、T2、T3、T4

True

True

L4

x=2、y=5、z=11

k=0、j=0

F1、F2、F3、F4

False

False

L1

MC/DC(修订的条件/分支软件测试)准则是一种实用的软件结构软件测试率软件测试准则,已被广泛应用于软件验证和软件测试过程中。

案例2-19:MC/DC覆盖测试。

condition和decision的概念:

if (A || B && C ) {
        语句1;
}
Else{
     语句2;
}

A,B,C都是一个条件,而(A || B && C)叫一个Decision,如果是条件软件测试,只需两个CASE,就能软件测试,就是让这个decision为True和False各一次,就能达到。即:

如果是MC/DC,就得4个测试用例,怎么计算呢?

MC/DC覆盖测试在每个判定中的每个条件都曾独立影响判定的结果至少一次(独立影响意思是在其他条件不变的情况下,改变一个条件):

A || B && C

总结:每个条件对结果都独立起作用。

(1)如果A对结果起作用的话,B必须为False、C必须为True,这样结果就独立受A的值影响。(A||0&&1)->(A||0),(A、B、C取值分别为A=True、B=False、C=True和A=False、B=False、C=True)。

(2)同理,如果B对结果独立起作用,A必须为False、C必须为True,两种情况B为True、False各一个 (0||B&&1) (A、B、C取值分别为A=False、B=True、C=True和A=False、B=False、C=True)。

(3)如果C独立对结果起作用,就是让(A || B) 为True,为了减少用例,上面的用例已经含有这样的用例了,就取A为False、B为True,这样C独立起作用的用例为 (0||1&&C)->(1&&C)。(A、B、C取值分别为A=False、B=True、C=True和A=False、B=True、C=False)。

可以看出,每个条件各走了一次True和False,这样3个变量条件就会有6个用例, 但是其中里面有两个是重复的。

因此,最后的测试用例为。

需要进一步补充说明的是,MC/DC测试的主要目的是为了防止在组合条件表达式中包含副作用(side effect),见以下语句:

if (a() || b() || c()){ ... }

当b函数或c函数产生副作用时,MC/DC软件测试存在非常大的必要性。

原则上不应在组合条件表达式中调用产生副作用的函数。

路径覆盖的含义是:选取足够多的软件测试数据,使程序的每条可能路径都至少执行一次(如果程序图中有环,则要求每个环至少经过一次)。

路径覆盖率的公式可以表示如下:

路径覆盖率=被执行的路径数量/所有路径数量×100%

案例2-20:路径覆盖(图2-16 )测试。

图2-16 路径覆盖

这里存在4条路径,分别为(1,3)、(1,4)、(2,3)、(2,4)。为了达到这些路径,设计测试用例见表2-25。

表2-25  路径覆盖测试用例

a

b

覆盖路径

1

1

1、3

1

0

1、4

0

1

2、3

0

0

2、4

回看本节开始的例子,设计什么数据可以使路径覆盖达到100%呢?第2.6.2节提到的两组测试用例,第一组测试用例分别执行了路径L4(abd)和L3(acd),第二组测试用例分别执行了路径L2(abe)和L1(ace)。所以,使用这4个测试用例,就可以达到路径覆盖测试100%。

路径覆盖测试用例见表2-26。

表2-26  路径覆盖测试用例

测试用例

原子条件
x>3、z<10

M1
x>3 &&z<10

原子条件
x=4、y>5

M2
x=4||y>5

覆盖路径

x=4、y=6、z=9

T1、T2

True

T3、T4

True

L4

x=4、y=5、z=10

T1、F2

False

T3、F4

True

L3

x=5、y=4、z=9

T1、T2

True

F3、F4

False

L2

x=4、y=5、z=10

F1、F2

False

F3、F4

False

L1

以上6种覆盖率强弱关系如图2-17所示。

图2-17 白盒测试各种覆盖的强度

控制流测试经常用在嵌入式软件系统。

案例2-21:控制流测试。

如图2-18所示。

图2-18 控制流软件测试的例子

首先:

然后进行总体排序

{1,2}、{1,3}、{1,4}、{2,5}、{2,6}、{3,5}、{3,6}、{4,5}、{4,6}、{6,2}、{6,3}、{6,4}。

 最后依次进行如下操作:

从1开始,5结束的连续序列,一直到把所有序列都输出完毕,见表2-27。

表2-27  控制流覆盖过程

操作

输出

挑选:{1,2} 、{2,5}
{1,2}、{1,3}、{1,4}、{2,5}、{2,6} 、{3,5}、{3,6}、{4,5}、{4,6} 、{6,2}、{6,3}、{6,4}

{1,2,5}

挑选:{1,3} {3,5}
{1,2}、{1,3}、{1,4}、{2,5}、{2,6} 、{3,5}、{3,6}、{4,5}、{4,6} 、{6,2}、{6,3}、{6,4}

{1,3,5}

挑选:{1,4} {4,5}
{1,2}、{1,3}、{1,4}、{2,5}、{2,6} 、{3,5}、{3,6}、{4,5}、{4,6} 、{6,2}、{6,3}、{6,4}

{1,4,5}

排选:{1,2} {2,6} {6,2} {2,5}
{1,2}、{1,3}、{1,4}、{2,5}、{2,6} 、{3,5}、{3,6}、{4,5}、{4,6} 、{6,2}、{6,3}、{6,4}

{1,2,6,2,5}

排选:{1,3} {3,6} {6,4} {4,6} {6,3} {3,5}
{1,2}、{1,3}、{1,4}、{2,5}、{2,6} 、{3,5}、{3,6}、{4,5}、{4,6} 、{6,2}、{6,3}、{6,4}

{1,3,6,4,6,3,5}

最后得到5个测试用例。

(1){1,2,5}。

(2){1,3,5}。

(3){1,4,5}。

(4){1,2,6,2,5}。

(5){1,3,6,4,6,3,5}。

在工作中,使用某些工具,可以通过图形化界面来了解各种测试的覆盖率。并且在单元测试中我们除了关心覆盖率的测试,还可以关心函数自身的功能测试。

案例2-22:单元测试中的基于代码的功能测试。

设计了函数float myAve(int32 a,int32 b),需求是获得a和b的平均数,如果输入参数有异常,则返回0.01。

可以设计这样一系列测试用例。

这样再去查看,测试工具告诉我们各种测试覆盖率是否达到100%。本书第二篇第8.1节“单元测试工具Junit4测试工具”中会介绍Junit 4测试工具。

白盒测试除了上述介绍的动态白盒测试外,还包括静态白盒测试,即代码审核。在静态审核中,代码书写规则非常重要,业界比较流行的编码规则请参看本篇附录C。

最后要指出白盒测试不仅在单元测试时进行,也可以在系统测试时进行,最早在嵌入式软件测试中,通过插桩的技术,通过工具得知各种覆盖率达到百分之几。现在上海有家公司对于非嵌入式产品,如APP程序,在运行系统测试时可以通过监控器,看到当时程序正在执行哪条语句,并且告诉各种覆盖率达到百分之几。这种技术叫作精准软件测试,是现在比较先进的软件测试方法。本书第二篇第11.4节“精准测试工具-星云测试平台”将会详细介绍。

首先申明,这句话十分有道理,但很多人都曲解了这句话的原意,认为“能够发现难以发现缺陷的测试用例才是好的测试用例”,却忘记了软件测试的目的所在,这是十分可怕的。笔者倾向于将测试用例当作一个集合来认识,对它的评价也只能对测试用例的集合来进行。软件测试本身是一种“确认和验证”的活动。软件测试需要保证以下两点。

因此,作为软件测试实施依据的测试用例,必须能完整覆盖软件测试的需求,而不应该针对单个测试用例去评判好坏。

讨论这个问题前,可以先考虑一下软件测试的目的。软件测试的目的之一是尽可能发现程序中存在的缺陷。软件测试活动本身也可以被看作是一个项目,也需要在给定的资源条件下尽可能达成目标。根据笔者个人的经验,大部分的国内软件公司在软件测试方面配备的资源都是不足够的,因此必须在软件测试计划阶段明确软件测试的目标,一切围绕软件测试的目标进行。

除了资源上的约束外,测试用例的详细程度也需要根据需要来确定。如果测试用例的执行者、测试用例设计者、测试活动相关人对系统了解都很深刻,那测试用例就没有必要写得太详细了,文档的作用本来就在于沟通,只要能达到沟通的目的,就可以了。

一般在软件测试计划中,软件测试设计的时间占30%~40%,软件测试设计工程师能够根据项目的需要自行确定用例的详细程度,在测试用例的评审阶段由参与评审的相关人对其把关。

想必没有一个人会认可这句话是正确的,但在实际情况中,却经常能发现这种想法的影子。笔者曾经参与过一个项目,软件需求和设计已经变更了多次,但测试用例却没有任何修改。导致的直接结果是新加入测试工程师在执行测试用例时不知所措,间接的后果是测试用例成了一堆废纸,开发工程师在多次被无效的缺陷报告打扰后,对软件测试工程师不屑一顾。

这个例子可能有些极端,但测试用例与需求和设计不同步的情况在实际开发过程中确是屡见不鲜的。测试用例文档是“活的”文档,这一点应该被软件测试工程师牢记。

在很多软件测试工程师编写的测试用例中,“预期输出”仅描述为程序的可见行为。其实,“预期结果”的含义并不只是程序的可见行为。例如,对一个订货系统,输入订货数据,单击“确定”按钮后,系统提示“订货成功”,这样是不是一个完整的用例呢?是不是系统输出“订货成功”,就应该作为唯一的验证手段呢?显然不是。订货是否成功还需要查看相应的数据记录是否更新,因此,在这样一个用例中,还应该包含对软件测试结果的显式的验证手段:在数据库中执行查询语句进行查询,看查询结果是否与预期一致。

测试用例是“一组输入、执行条件、预期结果”,毫无疑问地应该包括清晰的输入数据和预期输出。没有测试数据的用例最多只具有指导性的意义,不具有可执行性。当然,测试用例中包含的输入数据会带来维护、与软件测试环境同步之类的问题,关于这一点,《Effective Software Test》书中提供了详细的测试用例、软件测试数据的维护方法,供读者参考。

案例

所在章节

案例2-1:等价类的分类

2.1.1等价类

案例2-2:边界值的设计法

2.1.2边界值

案例2-3:公园门票规定

2.1.3基于输出的等价类/边界值划分

案例2-4:等价类测试

2.1.4测试用例的设计

案例2-5:由于边界值测试不完善带来的Bug

2.1.4测试用例的设计

案例2-6:日历等价类/边界值测试  

2.1.5案例

案例2-7:四边形类型判断系统

2.2.1四边形类型判断系统

案例2-8:用户登录系统

2.2.2用户登录系统

案例2-9:飞机票定价系统

2.2.3飞机票定价系统

案例2-10:视频播放机

2.3.1从状态转换图到状态转换树

案例2-11:电子商务购物

2.3.3业务流程状态转化法

案例2-12:文本编辑软件

2.4.1文本编辑软件

案例2-13:机票购买系统

2.4.2机票购买系统

案例2-14:网站兼容性组合测试

2.5运用正交法设计测试用例

案例2-15:语句测试覆盖率

2.6.1语句覆盖测试

案例2-16:分支测试覆盖率

2.6.2分支覆盖测试

案例2-17:条件测试覆盖率

2.6.3条件覆盖测试

案例2-18:判定/条件测试覆盖率

2.6.4判定/条件覆盖测试

案例2-19:MC/DC覆盖测试

2.6.5 MC/DC(修订的条件/分支软件测试)覆盖测试

案例2-20:路径覆盖测试

2.6.6路径覆盖测试

案例2-21:控制流测试

2.6.7控制流测试

案例2-22:单元测试中的基于代码的功能测试

2.6.8 单元测试中的基于代码的功能测试

  

扩展阅读:软件测试五大流派

 

  • 分析学派(Analytic School):认为软件是逻辑性的,将测试看作计算机科学和数学的一部分,结构化测试、代码覆盖率就是其中一些典型的例子。他们认为测试工作是技术性很强的工作,侧重使用类似UML工具进行分析和建模。
  • 标准学派(Standard School):从分析学派分支出来并得到IEEE的支持,把测试看作侧重劣质成本控制并具有可重复标准的、旨在衡量项目进度的一项工作,测试是对产品需求的确认,每个需求都需要得到验证。
  • 质量学派(Quality School):软件质量需要规范,测试就是过程的质量控制、揭示项目质量风险的活动,确定开发人员是否遵守规范,测试人员扮演产品质量的守门员角色。
  • 上下文驱动学派(Context-Driven School):认为软件是人创造的,测试所发现的每一个缺陷都和相关利益者(stakeholder)密切相关;认为测试是一种有技巧的心理活动;强调人的能动性和启发式测试思维。探索式测试就是其典型代表。
  • 敏捷学派(Agile School):认为软件就是持续不断的对话,而测试就是验证开发工作是否完成,强调自动化测试。TDD是其典型代表。


相关图书

现代软件测试技术之美
现代软件测试技术之美
渗透测试技术
渗透测试技术
JUnit实战(第3版)
JUnit实战(第3版)
深入理解软件性能——一种动态视角
深入理解软件性能——一种动态视角
云原生测试实战
云原生测试实战
Android自动化测试实战:Python+Appium +unittest
Android自动化测试实战:Python+Appium +unittest

相关文章

相关课程