全程软件测试之引子
在本书的开头,有必要先澄清对软件测试的理解,包括软件测试的核心价值和作用,以及软件测试和软件开发的关系等,帮助读者建立起软件测试的正确理念,这样对阅读和理解以后各章的内容会有很大帮助。
如何理解软件测试和建立起软件测试的正确理念呢?还是从软件测试的基本概念出发,回答下列几个问题,逐步揭示软件测试的内涵,深入到软件测试的核心价值观。这个过程,通过回答下列一系列问题来完成。
究竟什么是软件测试?
究竟什么是敏捷测试?
软件测试的作用是什么?
软件测试在软件开发生命周期(SDLC)中处在什么样的地位?
传统的软件测试过程是怎样的?
敏捷测试的过程又有什么不同?
下面就开始回答这些问题。即使您不能完全理解也不要急,后面各章会慢慢帮助您理解这些内容,掀开软件测试的神秘面纱。但有一点是明确的,当您在看完这段"引子"后,会对软件测试有一个整体的认识、正确的理解,从而不至于陷入"盲人摸象"的困境,也不至于陷入困惑的境地。
0.1 究竟什么是软件测试?
什么是软件测试?人们常常回答:软件测试就是发现软件产品中的Bug(缺陷)。也有人说,不对,软件测试是验证软件产品特性是否满足用户的需求。实际上,上述回答都没错,是对软件测试的正反两个方面的解释。
(1)软件测试就是发现软件产品中的Bug,强调测试人员以逆向思维方式,不断思考开发人员可能存在的误区、不良的习惯、系统的边界条件、异常输入和操作、系统弱点和漏洞等,更快地发现软件系统的问题。毕竟开发人员力求构造软件,以正向思维方式为主,所以测试人员从逆向思维出发,可以获得更高的测试效率。
(2)软件测试是验证软件产品特性是否满足用户的需求,是以正向思维方式针对软件系统的所有功能点,逐个验证其正确性,这是传统工业的质检工作在软件业的自然延伸。
但仅仅这样理解软件测试还不够,需要更全面的理解软件测试,就可以更好地做好工作,也可以适应不同的软件开发过程所带来的挑战,包括敏捷方法带给软件测试的极大挑战。
将近30年前,G.J.Myers在其经典著作《软件测试之艺术》(The Art of Software Testing)一书中,给出了测试的定义:"程序测试是为了发现错误而执行程序的过程"。那时对软件测试的认识还非常具有局限性,这也是受软件开发瀑布模型的影响,认为软件测试是编程之后的一个阶段。只有等待代码开发出来之后,通过执行程序,像用户那样操作软件发现问题,这就是"动态测试"。
如果在此时发现功能设计不合理或性能不好,就需要修改需求或修改设计,那就不得不返工到需求定义或系统设计阶段,造成很大的代价。所以,有必要将软件测试延伸到需求、设计阶段,即对阶段性成果--需求定义文档、设计技术文档进行验证,从而将动态测试延伸到静态测试,尽早地发现问题,把问题消灭在萌芽之中,将每个阶段产生的缺陷及时清除,极大地提高产品的质量,有效地降低企业的成本。静态测试就是在不运行软件系统时对软件或阶段性成果进行评审,包括需求评审、设计评审、代码扫描、代码评审等。
软件测试从"动态测试"延伸到"静态测试",是从狭义的软件测试发展到广义的软件测试,也使"软件测试"不再停留在编程之后的某个阶段上,而是贯穿整个软件开发生命周期(Software Development Life Cycle,SDLC)的质量保证活动。有了广义软件测试的概念,在敏捷开发中,软件测试就能被解释为对软件产品质量的持续评估。在敏捷方法中,不仅提倡持续集成,而且提倡持续测试,持续集成实际上也是为了持续测试。
从国际标准对软件测试的定义来看,软件测试被看做是"验证(Verification)"和"有效性确认(Validation)"这两类活动构成的整体,缺一不可。如果只做到其中一项,测试是不完整的。
(1)"验证"是检验软件是否已正确地实现了产品规格书所定义的系统功能和特性。验证过程提供证据表明软件相关产品与所有生命周期活动的要求相一致,即验证软件实现(即交付给客户的产品)是否达到了软件需求定义和设计目标。
(2)"有效性确认"是确认所开发的软件是否满足用户实际需求的活动。因为软件需求定义和设计可能就不对,上述一致性不能保证软件产品符合客户的实际需求,而且客户的需求也是在变化的,当需求定义是半年前确定的,这种变化的可能性就比较大。
软件不同于硬件,软件一般都是应用系统,常常和人们的娱乐、事务处理、商业活动、社区交流等紧密联系在一起,所以软件具有很强的社会性,所以有测试专家把测试和社会性、人类学等联系起来,认为软件测试是跨学科的(interdisciplinary)活动,以系统为焦点(systems-focused),通过不断调查(investigative)和讲故事(storytelling)方式完成软件质量的评估。通过其社会性,强调测试人员的思维能力和探索能力,强调测试的有效性和可靠性,在测试中要理解用户的行为、人们活动的背景和目的(上下文关系),不断观察,不断学习,发现和质量相关的信息(差异或质疑),从客户利益出发来守护产品的价值。
概括起来,究竟什么是软件测试呢?可以这样说,软件测试是贯穿整个软件开发生命周期、对软件产品(包括阶段性产品)进行验证和确认的活动过程,也是对软件产品质量持续的评估过程,其目的是尽快尽早地发现在软件产品(包括阶段性产品)中所存在的各种问题,尽最大可能地消除软件开发过程中所存在的产品质量风险。
0.2 究竟什么是敏捷测试?
先从敏捷开发这一方法论层次来讨论什么是敏捷测试,即敏捷测试有什么具体特征,或有哪些主要实践,然后再就目前非常热的敏捷具体框架Scrum来讨论Scrum中的敏捷测试(或称为Scrum Testing)。先研究如下敏捷宣言背后所蕴含的12条原则[43]。
(1)我们最重要的目标,是通过持续不断地及早交付有价值的软件使客户满意。
(2)欣然面对需求变化,即使在开发后期也一样。为了客户的竞争优势,敏捷过程掌控变化。
(3)经常地交付可工作的软件,相隔几星期或一两个月,倾向于采取较短的周期。
(4)业务人员和开发人员必须相互合作,项目中的每一天都不例外。
(5)激发个体的斗志,以他们为核心搭建项目。提供所需的环境和支援,辅以信任,从而达成目标。
(6)不论团队内外,传递信息效果最好、效率也最高的方式是面对面的交谈。
(7)可工作的软件是进度的首要度量标准。
(8)敏捷过程倡导可持续开发。责任人、开发人员和用户要能够共同维持其步调稳定延续。
(9)坚持不懈地追求技术卓越和良好设计,敏捷能力由此增强。
(10)以简洁为本,它是极力减少不必要工作量的艺术。
(11)最好的架构、需求和设计出自自组织团队。
(12)团队定期地反思如何能提高成效,并依此调整自身的举止表现。
这12条原则中没有一条直接谈到测试,那是否说明没有敏捷测试呢?有开发就有测试,只是原来参加敏捷宣言的17人,基本是清一色的程序员,没有在原则中单独阐述一下测试的原则。但如下的一些原则和测试的关联性很强。
(1)软件测试如何支撑或协助"持续不断地及早交付有价值的软件"?如何在非常有限的时间内进行充分的测试?这就是我们经常在敏捷测试中强调的"自动化测试",如果没有自动化测试,就没有敏捷,就不能持续不断地及早交付有价值的软件,而且还要"使客户满意"。
(2)"欣然面对需求变化,即使在开发后期也一样"和传统的开发原则是不同的,传统的开发希望有严格的需求变更控制,越到后期控制越严。而敏捷开发拥抱变化,那么测试如何适应这种变化?如何快速地完成回归测试?这可能要依赖于开发做好单元测试,或全员参与测试,以及全面支持系统级的、端到端的回归测试的自动化测试执行。
(3)传统的开发也要求"业务人员和开发人员必须相互合作",但存在一定的阶段性,例如前期需求评审、期间产品走查(product walk-through)、后期验收测试等要求有紧密的沟通与协作。但敏捷开发更强调"项目中的每一天都不例外",在这样的原则下,如何去做敏捷测试?这样可以减少测试文档,刚开始也没必要把测试计划写得很详细,而是写一页纸测试计划就可以,将来再持续地加以完善和调整。
(4)"可工作的软件是进度的首要度量标准",不再是测试计划完成情况、完成的测试用例数目、测试脚本量等,而是如何及时验证每天完成的功能特性。开发的工作量也不能按代码行来衡量,而是看多少个具体的用户故事(功能特性)被实现了(done)。某个开发说已完成了某个用户故事,要么是通过他自己的验证,要么是通过测试人员的验证,谁做的测试不重要,关键是要有准备好的测试,随时验证已完成的工作。
(5)"坚持不懈地追求技术卓越和良好设计",一方面要求测试的技术要不断提高,在处理每个测试任务时,都应该找到最有效的办法;另一方面,在前期要更多地参与设计评审,及时发现设计的问题。只有良好的设计,才能更好地支持系统的功能扩充和不断的重构。
基于这些原则,我们就可以概括敏捷测试的下列一些特点。
(1)敏捷测试一定是敏捷开发方法的一部分,应符合敏捷测试宣言的思想,也遵守上面所列的敏捷开发的原则,强调测试人员的个人技能,始终保持与客户/用户、其他成员(特别是业务人员、产品设计人员等)的紧密协作,建立良好的测试框架(特别是持续集成测试和自动化回归测试的基础设施)以适应需求的变化,更关注被测系统的本身而不是测试文档(如测试计划、测试用例等)。
(2)敏捷测试具有鲜明的敏捷开发的特征,如测试驱动开发(TDD)、验收测试驱动开发(ATDD),可以见我的另一篇文章《敏捷测试的思考和新发展》所讨论的。测试驱动开发的思想是敏捷测试的核心,或者说,单元测试是敏捷测试的基础,如果没有足够的单元测试就无法应付将来需求的快速变化、也无法实现持续的交付。这也说明,在敏捷测试中,开发人员承担更多的测试,这也就是我们说的,在敏捷测试中,是整个团队的共同努力。在敏捷测试中,可以没有专职的测试人员,每个人都可以主动去取设计任务和代码任务做,也可以去拿测试任务来做。在敏捷测试中,也可以像开发人员的结对编程那样,实践结对测试--一个测试人员对应一个开发人员、或一个测试人员对应另一个测试人员。
(3)敏捷测试无处不在、无时不在。在传统测试中也提倡尽早测试,包括需求和设计的评审;在传统测试里也提倡全过程测试。但在传统测试里阶段性特征相对突出一些,例如,需求评审,意味着先让产品人员去写需求,但需求文档写好之后,测试人员再参加评审。而在敏捷测试里,团队每一天都在一起工作,一起讨论需求、一起评审需求。在敏捷测试中,这种持续性更为显著一些。
(4)敏捷测试是基于自动化测试的,自动化测试在敏捷测试中占有绝对的主导地位。在传统测试中也提倡自动化测试,但由于传统开发的周期比较长(几个月到几年),即使没有自动化测试也是可以应付的,一般来说,回归测试能够获得几周时间,甚至1~2个月的时间。而敏捷测试的持续性迫切要求测试的高度自动化,在1~3天内就能完成整个的验收测试(包括回归测试)。没有自动化,就没有敏捷。
敏捷测试就是符合敏捷宣言思想,遵守敏捷开发原则,在敏捷开发环境下能够很好地和其整体开发流程融合的一系列的测试实践,这些实践具有鲜明的敏捷开发的特征,如TDD、ATDD、结对编程、持续测试等。敏捷测试和传统测试的区分,可以概括如下。
(1)传统测试更强调测试的独立性,将"开发人员"和"测试人员"角色分得比较清楚。而敏捷测试可以有专职的测试人员,也可以是全民测试,即在敏捷测试中,可以没有"测试人员"角色,强调整个团队对测试负责。
(2)传统测试更具有阶段性,从需求评审、设计评审、单元测试到集成测试、系统测试等,从测试计划、测试设计再到测试执行、测试报告等,但敏捷测试更强调持续测试、持续的质量反馈,阶段性比较模糊。
(3)传统测试强调测试的计划性,认为没有良好的测试计划和不按计划执行,测试就难以控制和管理,而敏捷测试更强调测试的速度和适应性,侧重计划的不断调整以适应需求的变化。
(4)传统测试强调测试是由"验证"和"确认"两种活动构成的,而敏捷测试没有这种区分,始终以用户需求为中心,每时每刻不离开用户需求,将验证和确认统一起来。
(5)传统测试强调任何发现的缺陷要记录下来,以便进行缺陷根本原因分析,达到缺陷预防的目的,并强调缺陷跟踪和处理的流程,区分测试人员和开发人员的各自不同的责任。而敏捷测试强调面对面的沟通、协作,强调团队的责任,不太关注对缺陷的记录与跟踪。
(6)传统测试更关注缺陷,围绕缺陷开展一系列的活动,如缺陷跟踪、缺陷度量、缺陷分析、缺陷报告质量检查等,而敏捷测试更关注产品本身,关注可以交付的客户价值。在快速交付的敏捷开发模式下,缺陷修复的成本很低。
(7)传统测试鼓励自动化测试,但自动化测试的成功与否对测试没有致命的影响,但敏捷测试的基础就是自动化测试,敏捷测试是具有良好的自动化测试框架支撑的快速测试。
0.3 软件测试的作用
在购买商品时,会发现商品上贴有一个"QC"标签,这就是产品经过质量检验(Quality Control)的标志。软件测试就好比制造工厂的质量检验工作,是对软件产品和阶段性工作成果进行质量检验,不仅验证产品是否符合事先的需求定义、设计要求和代码规范等,完成一致性的检查,而且要确认所实现的产品功能特性是否满足用户需求,每个功能特性都是用户真正所需要的。由于时间和预算的限制,我们无法证明一般的应用系统软件是没有问题的,而只能通过发现问题并消除这些问题来减低产品的质量风险、提高产品的质量。所以,软件测试是软件公司致力于衡量产品质量、保证产品质量的重要手段之一。
有人反驳说,质量是构建的,不是靠测试测出来的。没错,从"质量是构建的"角度看,开发人员对产品质量有更大贡献,测试对质量的贡献要低于开发工作,测试人员对质量的贡献要小。但这也不能否定测试的作用,测试人员帮助整个团队发现产品中存在的各种缺陷,然后督促开发人员消灭这些缺陷,软件产品的质量还是有显著的提高。如果从产品质量和质量责任来看,无论是把测试人员比作"产品质量守门员"还是比作"产品质量过程的监督者",都显示测试人员对产品质量有更大的责任,这是由"软件测试人员"这个角色所决定的,软件测试是质量保证的重要手段之一,许多公司也把测试人员放在质量保证(Quality Assurance)部门,甚至有的公司干脆就叫测试人员为QA人员。
概括起来,软件测试有以下四个方面的作用。
(1)产品质量评估:全面地评估软件产品的质量,为软件产品发布(验收测试)、软件系统部署(性能规划测试)、软件产品鉴定(第三方独立测试)委托方和被委托方纠纷仲裁(第三方独立测试)和其他决策提供产品质量所需的各种信息,也就是能够提供准确、客观、完整的软件产品质量报告。
(2)持续的质量反馈:通过持续的测试(包括需求评审、设计评审、代码评审等)可以对产品质量提供持续的、快速的反馈,从而在整个开发过程中不断地、及时地解决存在的质量问题,不断改进产品的质量,并减少各种返工,最大限度地降低软件开发的劣质成本。
(3)客户满意度的提升:通过测试发现所要交付产品的缺陷,特别是尽可能地发现各种严重的缺陷,降低或消除产品质量风险,提高客户的满意度,扩大市场份额,提高客户的忠诚度。
(4)缺陷预防:通过对缺陷进行分析,找出缺陷发生的根本原因(软件开发过程中所存在的流程缺失、不遵守流程、错误的行为方式、不良习惯等问题)或总结出软件缺陷模式,采取措施纠正深层次的问题,避免将来犯同样的错误,达到缺陷预防的效果,有效减少开发中出现的问题,提高开发的效率。
0.4 软件测试在SDLC中的位置
在著名的软件瀑布模型中,软件测试处在"编程"的下游,在"软件维护"的上游,先有编程后有测试,测试的位置很清楚,但瀑布模型没有反映SDLC的本质,没能准确无误地反映测试在SDLC的位置,瀑布模型中的软件测试是狭义的测试,落后的测试观念。
如前所述,软件测试贯穿整个SDLC,从需求评审、设计评审开始,就介入到软件产品的开发活动或软件项目实施中了。测试人员参与需求分析和需求评审,通过积极参与需求活动,测试人员不仅能发现需求定义自身存在的问题,而且能更深入理解业务需求、特定的用户需求和产品的功能特性,为测试需求分析与设计等打下坚实的基础。更进一步,这个阶段可以确定产品测试的验收标准,可以制定验收测试的计划和设计验收测试用例(test case)。同理,在软件设计阶段,测试人员要清楚地了解系统是如何实现的、采用哪些开发技术以及构建在什么样的应用平台之上等各类问题,这样可以提前准备系统的测试环境,包括硬件和第三方软件的采购。更要针对一些非功能特性(如性能、安全性、兼容性、可靠性等)检查系统架构设计的合理性和有效性,发现设计中存在的问题,并着手研究如何测试当前的软件系统,完成系统测试用例设计、测试工具的选型或启动测试工具的开发,进一步完善测试计划等。所有这些准备工作,都要花去很多时间,应尽早开展起来。
当设计人员在做详细设计时,测试人员可以直接参与具体的设计、参与设计的评审,找出设计的缺陷。同时,完成功能特性测试的用例,并基于这些测试用例开发测试脚本。
编程阶段的单元测试是很有效的,越来越得到业界的关注和实施。有数据显示,单元测试可以发现代码中60%~70%的问题,充分的单元测试可以大幅度提高程序质量。其次,单元测试和编程同步进行,极其自然,可以尽早发现程序问题。
软件测试在SDLC中的位置,可以通过图0-1充分地体现出来(也可参考W模型)。软件测试和软件开发构成一个全过程的交互、协作的关系,两者自始至终一起工作,共同致力于同一个目标--按时、高质量地完成项目。
如果从团队来看,测试人员也是软件研发团队中的主力军。在软件研发团队中,虽然有很多角色,包括项目经理、产品经理、用户界面(User Interface, UI)设计人员、开发人员、测试人员、软件配置管理人员等,但从构成的人数比例来看,主要以开发人员和测试人员为主。在传统的软件开发开发中,特别是一些关键系统(如操作系统、银行业务系统、交通信号控制系统等),测试人员占的比例就比较高,以微软公司作为典型的例子,在其过去10~20年开发Windows操作系统和Office产品开发团队中,开发人员和测试人员比例是1:2。相反的例子就是Google和facebook等互联网软件,如Google研发团队中,开发人员和测试人员比例是10:1,而facebook没有专职的测试人员。开发人员和测试人员比例,也是经人们讨论的热门话题,为此我还写过两篇文章,在自己的博客[28]上发表,但基本观点是因为每个公司、公司的每个产品、产品的各个项目或各个阶段都不同,没法用一个比例来衡量不同的测试团队,因为这个比例受"开发人员做哪些工作(是否包括比较多的测试工作)、开发交付的质量、产品质量要求(不同的产品质量要求不一样)、开发模式"等多种因素影响,例如:
开发人员进行了足够的单元测试,测试人员可以大大减少;
非使命攸关系统、非生命攸关系统,对软件质量要求可以降低,测试人员可以继续减少;
如果是在线服务系统,可以随时交付新的版本,修复缺陷的成本低、速度快,测试人员还可以继续减少
总之,具体案例要具体分析。
0.5 传统的软件测试过程
在上节讨论"软件测试在SDLC中的位置"时,已触及软件测试过程,那个改进的V模型(图0-1)就反映了测试过程。但为了更明确测试过程,从两条线分别展示软件测试的基本过程,如图0-2所示。
(1)一条线是从软件工程过程来看,经过需求评审、设计评审、代码评审与单元测试、集成测试、系统测试和验收测试阶段。
(2)另一条线是从项目管理角度看,经过测试计划、测试设计、测试执行与监控、测试结果分析与评估(报告)和项目总结阶段。
过程的描述尽量简单,从而使读者一目了然,基本知道各个环节主要的工作,但实际许多工作是交替进行或同时进行的,例如在图0-1中所描述的,系统测试和验收测试的设计工作很早就开始了,如图0-1所示,系统测试和验收测试的设计工作分别在需求评审、设计评审阶段就可以开展,大部分内容可以完成,在后续时间还可以继续完善。
因为提倡每日构建或持续集成,如果仅从软件代码角度看,单元测试和集成测试是同时进行的,没有单独的集成测试阶段。但如果考虑和其他子系统的集成、和第三方系统集成、和硬件集成等工作,集成测试的阶段还是存在的。
在实际操作中,还可以定义自己所需的阶段,或里程碑。这里以曾在Webex公司研发流程中所定义的测试过程作为示例,让大家更好理解传统的软件测试过程,如图0-3和表0-1所示。在这个示例中,主要的里程碑有:
产品需求文档(PRD)或市场需求文档(MRD)的评审和签发;
产品规格说明书(Spec)的评审和签发;
测试计划、测试计划书的评审和签发;
测试用例的设计、评审和签发;
功能测试;
系统测试;
验收测试。
0.6 敏捷测试过程
上面讨论了传统的软件测试过程,下面就简单介绍一下敏捷测试过程,正文中还会有更多的讨论。在敏捷测试流程中,参与单元测试,关注持续迭代的新功能,针对这些新功能进行足够的验收测试,而对原有功能的回归测试则依赖于自动化测试。由于敏捷方法中迭代周期短,测试人员尽早开始测试,包括及时对需求、开发设计的评审,更重要的是能够及时、持续地对软件产品质量进行反馈。简单地说,在敏捷开发流程中,阶段性不够明显,持续测试和持续质量反馈的特征明显,这可以通过图0-4来描述。
如果再具体一些,使流程更具可操作性,需要以敏捷Scrum为例,来介绍敏捷测试的流程。先看看Scrum流程,从图0-5中可以看出,除了最后"验收测试"阶段,其他过程似乎没有显著的测试特征,但隐含的测试需求和特征还是存在的。
(1)ProductBacklog (需求定义阶段),在定义用户需求时测试要做什么?测试需要考虑客户的价值大小(优先级)、工作量基本估算之外,需要认真研究与产品相关的用户的行为模式(如Behavior Driven Development,BDD),产品的质量需求,哪些质量特性是我们需要考虑的?有哪些竞争产品?这些竞争产品有什么特点(优点、缺点等)?
(2)SprintBacklog(阶段性任务划分和安排),这时候需要明确具体要实现的功能特性和任务,作为测试,这时候要特别关注"Definition of Done",即每项任务结束的要求--即任务完成的验收标准,特别是功能特性的设计和代码实现的验收标准。ATDD(使用验收测试驱动开发)的关键一步也体现在这里,在设计、写代码之前,就要将验收标准确定下来。一方面符合测试驱动开发思想,最初就要把事情做对,预防缺陷;另一方面持续测试和验收测试的依据也清楚了,可以快速做出测试通过与否的判断。
(3)在每个迭代(Sprint)实施阶段,主要完成Sprint Backlog所定义的任务,这时除了测试驱动开发(TDD)或单元测试之外,应该进行持续集成测试或通常所说的BVT(Build Verification Test)。而且开发人员在设计、写代码时都会认真考虑每一组件或每一代码块都具有可测试性,因为测试任务可能由他们自己来完成。如果有专职的测试人员角色,一方面可以完善单元测试、集成测试框架,协助开发人员进行单元测试;另一方面可以按照针对新实现的功能特性进行更多的探索式测试,同时开发验收测试的脚本。如果没有专职的测试人员角色,这些事情也是要完成的,只是由整个团队共同完成。虽没有工种的分工,但也存在任务的分工。
(4)验收测试可以由自动化测试工具完成,但一般情况下,不可能做到百分之百的自动化测试。例如易用性测试就很难由工具完成。即使对性能测试,是由工具完成,但还需要人来设计测试场景,包括关键业务选择、负载模式等。敏捷的验收测试和传统的验收测试不同,侧重对"Definition of Done"的验证,但基本的思想和传统开发是一致的,任何没有经过验证的产品特性是不能直接发布出去的。
--------本文节选自《全程软件测试(第2版)》