软件测试就是挑Bug?也许你有认知偏差
“什么是软件测试?”这个看似简单的一个问题,其实也是最难的问题。说它简单,是因为这是一个基本的问题,做软件测试工作多年的小伙伴,自然知道什么是软件测试。说它难,是因为“软件测试”有很多内涵,要了解其全部内涵,并非那么容易。如果我们去问软件研发人员什么是软件测试,得到的答案可能五花八门,人们对软件测试有不同的理解。现在最常见的理解就是:软件测试就是找bug、发现缺陷。
但也有人会认为软件测试就是:
检查软件产品是否符合设计要求;
验证软件产品需求、设计和实现的一致性;
确认软件产品是否满足用户的实际需求;
对软件产品质量的全面评估;
提供软件产品质量信息;
揭示软件产品的质量风险;
投入较低的保障性成本极大地降低劣质成本;
验证与确认;
调查、分析和比较;
不断探索;
……
有太多的理解,而且都没有错,只是看问题的角度不一样。虽然回答问题时,也容易脱口而出,不会仔细斟酌,只看到软件测试的一面,没有系统地分析“什么是软件测试”。
下面我们就好好讨论“什么是软件测试”,因为有什么理解就有什么行动。有正确的理解,就有正确的操作;相反,有错误的理解,就有错误的操作。所以,先帮助读者对“软件测试”建立正确、全面的认识,构建起一个完整的“软件测试”轮廓,不至于陷入“盲人摸象”的困境,对软件测试有片面的理解。然后,我们再展开流程、方法、技术和实践的讨论。也就是在全面讨论“全程软件测试”之前,咱们需要找到共同语言,即对软件测试的一些基本概念达成共识,为后面的沟通扫除障碍。
软件测试基本认知——正反思维
什么是软件测试?人们常常回答:软件测试就是发现软件产品中的bug(缺陷)。也有人说,不对,软件测试是验证软件产品特性是否满足用户的需求。实际上,上述回答都没错,是对软件测试的正反两个方面的解释。
早期,人们更多的是将“测试”看作是对产品的“检验”,检查软件的每个功能是否运行正常。正如1983年Bill Hetzel将软件测试定义为:“软件测试就是一系列活动,这些活动是为了评估一个程序或软件系统的特性或能力,并确定其是否达到了预期结果。”从这个定义中,至少我们可以看到以下两点。
测试试图验证软件是“工作的”,也就是验证软件功能执行的正确性。
测试的活动是以人们的“设想”或“预期的结果”为依据。这里的“设想”或“预期的结果”是指需求定义、软件设计的结果。
但同时我们知道,软件测试有一条原则:测试是不能穷尽的。测试会面对大量的测试数据、测试场景或代码路径等,测试也只是一个样本实验,不能证明软件是正确的,只能说明发现的缺陷的确是缺陷。但如果没有发现问题,并不能说明问题就不存在,而是至今未发现软件中所潜在的问题。正如《软件测试的艺术》一书作者Glenford J. Myers所说,测试不应该着眼于验证软件是工作的,相反,应该用逆向思维去发现尽可能多的错误。他认为,从心理学的角度看,如果将 “验证软件是工作的”作为测试的目的,非常不利于测试人员发现软件的错误。因此,1979年他给出了软件测试的不同的定义:“测试是为了发现错误而执行一个程序或者系统的过程。”从这个定义可以看出,假定软件总是存在缺陷的(事实上也是如此)、有错误的,测试就是为了发现缺陷,而不是证明程序无错误。
从这个定义延伸出去,一个成功的测试是发现了软件问题的测试,否则测试就没有价值。这就如同一个病人(因为是病人,假定确实有病),到医院去做相应的检查,结果没有发现问题,那说明这次体检是失败的,浪费了病人的时间和金钱。以逆向思维方式引导人们证明软件是“不工作的”,会促进我们不断思考开发人员对需求理解的误区、不良的习惯、程序代码的边界、无效数据的输入等,找到系统的薄弱环节或识别出系统复杂的区域,目标就是发现系统中各种各样的问题。
人类的活动具有高度的目的性,建立适当的目标具有显著的心理作用。如果测试目的是为了证明程序里面没有错误,潜意识里就可能不自觉地朝这个方向去做。在进行测试的过程中,就不会刻意选择一些尽量使程序出错的测试数据,而选择一些常用的数据,测试容易通过,而不容易发现问题。如果测试的目的是要证明程序中有错,那我们会设法选择一些易于发现程序错误的测试数据,这样,更早、更快地发现缺陷。毕竟开发人员力求构造软件,以正向思维方式为主,所以逆向思维方式可以提升我们的测试效率。
逆向思维也有不利的一面,容易陷于局部的深度测试,缺乏广度。因为觉得某个地方有缺陷,就对这个地方进行测试,然后不断深入下去,这样容易忽视一些区域。虽然那些地方产生的缺陷不多,但如果产生了严重缺陷,也是我们不能承受的。所以正向思维也是有价值的,它会督促针对软件系统的所有功能点,逐个验证其正确性,哪个功能越重要越要进行检验。正向思维会让我们的测试更有广度——良好的测试覆盖面。
为了做好测试,既要有深度,又要有广度;既要有效率,又要有测试工作自身完整的质量。所以,我们应该将正向思维和逆向思维有机地结合起来,做到效率和质量的平衡。换句话说,当我们需要效率时,更多采用逆向思维,当我们需要测试广度来确保完整的测试质量时,则多采用正向思维。这种平衡还体现在不同的应用领域,例如国防、航天、银行等关键性软件系统,承受不了系统的任何一次失效。因为这些失效完全有可能导致灾难性的事件,所以强调验证(verify),以保证非常高的软件质量。而一般的商业应用软件或服务,质量目标设置在“用户可接受水平”,以降低软件开发成本,加快软件发布速度,有利于市场的扩张,则可以强调逆向思维,尽快找出大部分缺陷。
从狭义测试到广义测试
前面提到Glenford J. Myers,他早期给软件测试的简单定义是:“程序测试是为了发现错误而执行程序的过程”,也体现出当时对软件测试的认识非常具有局限性。这也是受软件开发瀑布模型的影响,认为软件测试是编程之后的一个阶段。只有等待代码开发出来之后,通过执行程序,像用户那样操作软件发现问题,这就是“动态测试”。
对于需求阶段产生的缺陷,在不同阶段发现和修复的成本是不一样的。如果在需求阶段发现需求方面的缺陷并进行修复,只要修改需求文档,其成本很低。需求阶段产生的缺陷,如果在需求阶段没有发现,等待设计完成之后才被发现,就需要修改需求和设计,成本增大。需求阶段产生的缺陷,如果在需求和设计阶段都没有发现,等待代码写完之后才被发现,就需要修改需求、设计、代码,成本就更大。设计上的问题,在设计阶段被发现,只要修改设计,如果在后期发现,返工的路径就变长了,其修复的成本自然就增大。缺陷发现得越迟,其修复的成本就越高,如图1-1所示,呈现了不同阶段产生的缺陷在不同阶段修复的成本,所以这要求我们尽早发现缺陷。
图1-1 不同阶段产生的缺陷在不同阶段修复的成本
为了尽早发现缺陷,我们有必要将软件测试延伸到需求、设计阶段,即对软件产品的阶段性成果——需求定义文档、设计技术文档进行评审或验证。这不同于软件质量保证(Quality Assurance,QA),虽然QA侧重评审,但它重点评审流程、评审管理,包括对需求、设计、编码和测试过程规范性的评审。而这里提到的需求和设计的评审依旧是对软件产品的检验或验证,只是需求文档和设计文档只是软件产品的阶段性产品。如果按照“软件=程序+文档+数据结构”这样的定义,需求文档和设计文档等也属于软件的组成部分,软件测试自然也包括需求和设计的验证。
基于上述考虑,将早期的动态测试延伸到静态测试,即从狭义的软件测试发展到广义的软件测试。
狭义的软件测试:动态测试——运行程序而进行的测试,测试只是编程之后的阶段,这也是由传统的瀑布模型而决定的。
广义的软件测试:动态测试+静态测试,将需求评审、设计评审、代码评审(含代码的静态分析)等也纳入软件测试工作之中。这也使“软件测试”不再停留在编程之后的某个阶段上,而成为贯穿整个软件研发周期的质量保证活动,这也是本书“全程软件测试”的最早立意所在。
静态测试就是在不运行软件系统时对软件或阶段性成果进行评审,包括需求评审、设计评审、代码评审等。引入静态测试,就可以尽早地发现问题,把问题消灭在萌芽之中,将每个阶段产生的缺陷及时清除,极大地提高产品的质量,有效地降低企业的成本。
基于质量的认知
软件测试虽然不能等同于软件质量保证(SQA),但它是软件质量保证的主要手段之一。当我们讨论软件测试时,绝对离不开“质量”。基于质量的认识,软件测试就是对软件产品的质量评估,提高软件产品有关的质量信息。即使从1.1节中我们认为软件测试就是发现软件产品中的bug(缺陷),哪什么是“缺陷”呢?简单地说,缺陷就是质量的对立面,一切违背质量的问题都可以看作软件缺陷(虽然从专业术语来仔细辨析的话,会将问题分为“内在错误,外部失效”等)。所以要理解软件测试,就必须理解软件质量。
说起“质量”这个概念,我们都很熟悉,会说“坏的质量会怎样怎样,好的质量会怎样怎样”,但让我们给出质量的正式定义,可能不是容易的事情。我们也可以查国际标准,了解如何给质量下定义。例如IEEE Std 829-2008定义质量就是系统、组件或过程满足特定需求的程度,满足客户/用户需求或期望的程度。满足程度越高,质量就越好。例如,从软件需求定义文档来看,它所描述的需求和客户实际业务需求越吻合,将来实现的软件越有可能满足客户的业务需求,也意味着需求文档的质量越高。但这样说,还是比较宽泛,很难衡量质量。那究竟如何评估质量?从哪些维度来衡量质量呢?这就引出质量模型。基于质量模型,我们可以清楚质量有哪些属性(或维度),然后针对这些属性逐个地进行评估,不需要对软件质量进行整体评估,相当于按质量的各个维度来进行评估、各个击破。
过去将软件质量分为内部质量、外部质量和使用质量,像代码的规范性、复杂度、耦合性等可以看作是内部质量,内部质量和外部质量共用一个质量模型。现在国际/国家标准将内部质量和外部质量合并为产品质量。产品质量可以认为是软件系统自身固有的内在特征和外部表现,而使用质量是从客户或用户使用的角度去感知到的质量。因为质量是相对客户而存在,没有客户就没有质量,质量是客户的满意度。过去认为,内部质量影响外部质量、外部质量影响使用质量,而使用质量依赖外部质量、外部质量依赖内部质量。今天可以理解为产品质量影响使用质量,而使用质量依赖产品质量。
1.产品质量
根据国际标准IEEE 24765-2010,产品质量是指在特定的使用条件下产品满足明示的和隐含的需求所明确具备能力的全部固有特性。而根据ISO 25010:2011标准,质量模型从原来的6个特性增加到8个特性,新增加了“安全性、兼容性”。如图1-2所示,蓝色标注的内容属于新增加或改动的内容。这里的安全性是指信息安全性(Security),原来放在“功能性”下面,但现在绝大部分产品都是网络产品,安全性越来越重要,所以有必要作为单独的一个维度来度量。今天系统互联互通已经很普遍,其次终端设备越来越多,除了传统的PC机,还有许多智能移动设备,如手机、平板电脑、智能手环、智能手表等,这些都要求系统具有良好的兼容性。这些特性就对应着测试类型,如功能测试、性能测试(效率)、兼容性测试、安全性测试等。
图1-2 ISO 25010 2016 产品质量模型
功能适应性(functional suitability):软件所实现的功能达到其设计规范和满足用户需求的程度,强调正确性、完备性、适合性等。
效率(efficiency):在指定条件下,软件对操作所表现出的时间特性(如响应速度)以及实现某种功能有效利用计算机资源(包括内存大小、CPU占用时间等)的程度,局部资源占用高通常是性能瓶颈存在;系统可承受的并发用户数、连接数量等,需要考虑系统的可伸缩性。
兼容性(compatibility),涉及共存和互操作性,共存要求软件能给与系统平台、子系统、第三方软件等兼容,同时针对国际化和本地化进行合适的处理。 互操作性要求系统功能之间的有效对接,涉及API和文件格式等。
易用性(usability):对于一个软件,用户学习、操作、准备输入和理解输出所做努力的程度,如安装简单方便、容易使用、界面友好,并能适用于不同特点的用户,包括对残疾人、有缺陷的人能提供产品使用的有效途径或手段(即可达性)。
可靠性(reliability):在规定的时间和条件下,软件所能维持其正常的功能操作、性能水平的程度/概率,如成熟性越高,可靠性就越高;用平均失效前时间(Mean Time To Failure,MTTF)或平均故障间隔时间(Mean Time Between Failures,MTBF)来衡量可靠性。
安全性(security):要求其数据传输和存储等方面能确保其安全,包括对用户身份的认证、对数据进行加密和完整性校验,所有关键性的操作都有记录(log),能够审查不同用户角色所做的操作。它涉及保密性、完整性、不可抗抵赖性、可审核性、真实性。
可维护性(maintainability):当一个软件投入运行应用后,需求发生变化、环境改变或软件发生错误时,进行相应修改所做努力的程度。它涉及模块化、可复用性、易分析性、易修改性、易测试性等
可移植性(portability):软件从一个计算机系统或环境移植到另一个系统或环境的容易程度,或者是一个系统和外部条件共同工作的容易程度。它涉及适应性、可安装性、可替换性。
2.使用质量
从ISO/IEC 25010标准看,软件测试还要关注使用质量,如图1-3所示。在使用质量中,不仅包含基本的功能和非功能特性,如功能(有效与有用)、效率(性能)、安全性等,还要求用户在使用软件产品过程中获得愉悦,对产品信任,产品也不应该给用户带来经济、健康和环境等风险,并能处理好业务的上下文关系,覆盖完整的业务领域。
图1-3 使用质量的属性描述
为了便于理解使用质量,下面举3个例子。
【例1-1】我自己亲身经历的例子。我在手机上安装了一个英语学习软件,自动下载该款软件用到的多个语音库(如新概念英语、六级英语等),它在我讲课时,但并没有判断我手机连接的是Wi-Fi还是3G/4G,造成我的流量大大超过套餐额度,产生了额外的300元流量费。从功能上看,自动下载是一个不错的功能,但有很大的经济风险,在使用质量上有明显缺陷。
【例1-2】当我们玩游戏,沉醉于某款游戏,从产品本身质量属性看。是一个好产品,没有问题。但从使用质量看,会有损于玩家的健康,有健康风险,所以需要设置防沉迷功能。
【例1-3】当我们使用百度地图、滴滴打车等软件时,往往是在大街上。如果站在人行道或安全地方使用没问题,但是如果一面横穿马路一面还在使用,就有安全风险。这类软件应该给予提示,否则它们要承担相应的风险责任。
基于风险的认知
因为没有办法证明软件是正确的,软件测试本身总是具有一定的风险性,所以软件测试被认为是对软件系统中潜在的各种风险进行评估的活动。从风险的观点看,软件测试就是对软件产品质量风险的不断评估,引导软件开发工作,进而将最终发布的软件所存在的风险降到最低。基于风险的软件测试认知主要体现在两点上:
软件测试不仅仅停留在单个缺陷上,要从所发现的问题看到(分析出)某类质量风险或某个具有潜在风险的区域。
软件测试被看作是一个动态的质量监控过程,对软件开发全过程进行检测,随时发现不健康的征兆,及时评估新的风险,设置新的监控基准,不断地持续下去。
基于风险对测试的认知,会强调测试的持续性,持续地进行测试,写几行代码就要做测试、实现一个功能就要对这个功能进行测试,开发和测试相伴而行。这种认知特别适合敏捷开发模式下的测试——敏捷测试。在敏捷开发中,软件测试就能被解释为对软件产品质量的持续评估。在敏捷方法中,不仅提倡持续集成,而且提倡持续测试,持续集成实际上也是为了持续测试。
基于风险对测试的认知还不断提醒我们:在尽力做好测试工作的前提下,工作有所侧重,在风险和开发周期限制上获得平衡。首先评估测试的风险,每个功能出问题的概率有多大?根据Pareto原则(也叫80/20原则),哪些功能是用户最常用的20%功能?如果某个功能有问题,其对用户的影响又有多大?然后根据风险大小确定测试的优先级。优先级高的功能特性,测试优先得到执行。一般来讲,针对用户最常用的20%功能(优先级高)的测试会得到完全地、充分地执行,而低优先级功能的测试(另外用户不常用的80%功能)就可能由于时间或经费的限制,测试的要求降低、减少测试工作量。
基于社会的认知
软件不同于硬件,软件一般都是应用系统,常常和人们的娱乐、事务处理、商业活动、社区交流等紧密联系在一起,所以软件具有很强的社会性,所以有必要把心理学、人类学和社会学等引入到软件测试中。软件测试不仅仅是技术活动,而且是社会、心理等综合性活动,软件测试是跨学科的(inter-disciplinary)活动,以系统为焦点(systems-focused),通过不断调查(investigative)和讲故事(storytelling)的方式完成软件质量的评估。
通过软件测试的社会性认知,强调测试人员的思维能力和探索能力,强调测试的有效性和可靠性,在测试中要理解用户的行为、人们活动的背景和目的(上下文关系),不断观察,不断学习,发现和质量相关的信息(差异或质疑),从客户利益、业务特性出发来守护产品的价值。
也正是由于软件测试的社会性,需要对软件产品的易用性、免于风险的程度、上下文覆盖等进行验证。在易用性测试中,人们常常进行A/B测试,给出不同的解决方案(UI布局、功能设计等),向不同的用户群发布产品,来检测哪个解决方案更受用户喜欢。
基于经济的认知
一般来说,一个软件产品没有经过测试是不会发布(release)、不会部署(deploy)到产品线上,或者说,不敢发布、不敢上线。因为在当前的开发模式和开发技术情况下,人们开发的软件存在严重的缺陷绝对是大概率事件。如果没有经过测试,就发布出去,可能软件根本不能用、不好用,或者用起来出现各种各样的问题,用户满意度很低,给产品造成负面影响,甚至给客户带来严重的经济损失或影响到用户的生命安全。
从经济观点看,软件缺陷会给企业带来成本,这个成本就叫劣质成本(Cost of Poor Quality,COPQ)。基于经济的认知,软件测试就是通过投入较低的保障性成本来降低劣质成本,帮助企业获得利润。高质量不仅是有竞争力,而且是带来良好的经济收益的。例如苹果手机就是以其高质量获得比其他品牌手机更高的利润率。据相关媒体统计数据看,苹果智能手机在高端手机市场只占四分之一,但利润占到一半。
测试的经济观点就是如何以最小的代价获得更高的收益,这也要求软件测试尽早开展工作,发现缺陷越早,返工的工作量就越小,所造成的损失就越小。所以,从经济观点出发,测试不能在软件代码写完之后才开始,而是从项目启动的第一天起,测试人员就参与进去,尽快尽早地发现更多的缺陷,并督促和帮助开发人员修正缺陷。
基于标准的认知
软件测试被视为“验证(Verification)”和“有效性确认(Validation)”这两类活动构成的整体,缺一不可。如果只做到其中一项,测试是不完整的。
“验证”是检验软件是否已正确地实现了产品规格书所定义的系统功能和特性。验证过程提供证据表明软件相关产品与所有生命周期活动的要求相一致,即验证软件实现(即交付给客户的产品)是否达到了软件需求定义和设计目标。
“有效性确认”是确认所开发的软件是否满足用户实际需求的活动。因为软件需求定义和设计可能就不对,上述一致性不能保证软件产品符合客户的实际需求,而且客户的需求也是在变化的,当需求定义是半年前确定的,这种变化的可能性就比较大。
对验证和确认有不同的解释。简单地说,单元测试、集成测试和系统测试都可以理解为“验证”,都是基于需求定义文档和设计规格说明书文档来进行验证;而验收测试则在用户现场、由用户共同参与进行,可以理解为“有效性确认”,因为之前的需求定义和设计都可能存在错误,研发团队没有正确理解用户的原意(用户的真实期望),仅仅根据需求定义文档和设计规格说明书文档来完成测试,并不能代表所实现的功能特性是用户真正想要的。而在验收测试中,用户参与进来,是可以确认所实现的功能特性是否是用户真正想要的。
另一种解释是根据图1-4所示的V模型,验证是架构设计评审、详细设计评审和代码评审/单元测试,分别验证架构设计是否和需求一致、详细设计是否和架构设计一致、代码是否和详细设计一致,用左边带箭头的粗虚线表示。而有效性确认则是集成测试、系统测试、验收测试,如中间带箭头的细虚线表示。
图1-4 软件研发的V模型
图1-4 软件研发的V模型
另一种解释是根据图1-4所示的V模型,验证是架构设计评审、详细设计评审和代码评审/单元测试,分别验证架构设计是否和需求一致、详细设计是否和架构设计一致、代码是否和详细设计一致,用左边带箭头的粗虚线表示。而有效性确认则是集成测试、系统测试、验收测试,如中间带箭头的细虚线表示。
[美] 朱少民 著
本书系统地总结了过去十年中软件测试发生的变化,浓缩了作者许多宝贵的软件测试经验。本书首先介绍对于软件测试的不同看法,全程软件测试的思想,软件测试的基础设施与TA框架、团队能力建设;然后逐步深入到测试的计划、设计、执行、持续反馈和改进;接着,讨论全程测试的思想,包括全程静态测试、全程性能测试、全程安全性、全程建模、全程可视化。本书最后展望了软件测试的未来。
本书适合软件测试人员阅读,也可作为相关专业人士的参考指南
本文转载自异步社区。
原文链接:https://www.epubit.com/articleDetails?id=N433ecb71-ddf9-4950-b48a-81f4694f049e
- 点赞
- 收藏
- 关注作者
评论(0)