1.3 从专家到高手
小艾成为软件测试工程师加入项目团队已有一段时间,参加的几个开发项目给他带来了一些实践经验。时常倾听同事的经验介绍,让他有机会对自己所处的水平做出一个合理的判断。随着软件测试基本理论及实践经验的积累,小艾感觉自己跟刚加入时已经有明显的区别。这种区别体现在几个方面,包括对组织结构的认识、对工作内容和工作方法的理解、对测试相关的专业技能的掌握等。软件测试工程师是一个技术背景非常强的职位,因此,技术是这个职位的立足点。尽管没有详细实践过不同的测试,但小艾已经对测试的来龙去脉有了比较系统的了解,对于测试的关键点有了清楚的认识。应该说,小艾逐渐从测试菜鸟成长为一名测试专家。
对于在技术上进一步修炼的方向,小艾依然有自己的疑惑。究竟达到什么样的程度才算是脱离“菜鸟”头衔而成为一名专家呢?专家是否就意味着技术上已经达到了顶峰?优秀的测试工程师应该有哪些标准?
带着这些疑问,小艾又找到他的导师凯文。面对有着类似经历的新人,凯文一直都是知无不言,言无不尽的。这种分享的氛围,使软件开发实验室成为一个非常适合学习和交流的地方。
凯文的解释从区分新手、专家和高手三个级别谈起。
刚开始接触一个新的领域时,对这个领域一无所知或者知之甚少,对于这个领域,我们就是新手。正如之前已经提及的,术业有专攻,同一个人,在一个领域是新手,并不妨碍他在另一个领域是专家。作为测试工程师新手,要成长为专家,需要对测试的相关专业技能进行系统的学习和实践。在开发项目组里,测试团队本身就包括多个角色,对于每种角色,从技能水平上也有新手和专家的区分。新手到专家的学习曲线主要包括学习测试和开发方法、测试计划和测试设计、测试文档的编写、发现和解决问题的一般方法等。在从新手到专家的发展过程中,“准专家”可以在专家的指导下完成特定的测试任务,能发现和解决一些比较常见的问题。随着经验的积累,测试新手可以成长为测试专家。
软件测试的关键考量点是软件的质量,因此,对于软件工程师而言,经验积累是新手成长为专家的过程中不可缺少的环节。当测试新手对测试的相关技能都熟练掌握、经验积累也达到一定程度之后,新手就基本上成长为测试专家。测试专家对相关的测试技能和工具都已经熟练地掌握,能够以标准化的方式策划并完成测试任务。专家的“专业”,主要体现在他对软件质量的控制把握方面的专业。
而高手则是专家更进一步的发展方向。专家的特点表现在对流程的严格把握,通过一种控制力保证软件的质量;而高手则比这走得更远。有一些非常复杂的系统或复杂的应用场景,已经超出了正常流程的控制范围。在这种条件下依然要保证系统能够正常满足需求,于是对测试人员提出了更高的要求。高手的价值就在这种超越一般情形的条件下体现出来了。总体而言,高手对于发现问题、解决问题,有更多超出一般步骤的灵感和直觉,这种灵感和直觉是建立在对系统的深入理解及高手本身强大的洞察力基础上的。
如果说新手到专家的成长过程是一个学习“硬性技能”的过程,那么专家成长为高手则更在于“软性技能”的修为。软性技能的提高,归根结底是思考能力和分析能力的提高。新手可以随着经验积累和技能学习成长为专家,然而,经验的继续积累只是专家成长为高手的其中一部分。专家到高手的修炼,重点在于思考的方法和技巧。
1.3.1 像外行一样思考,像专家一样实践
如果抛开测试的具体技术和实现细节,只是关注测试的目的,那么测试的本质其实就是发现问题和解决问题的过程。在讲述问题分析的方法时,我们介绍过自顶向下和自底向上的方法。作为对一般性问题的分析方法,这两种方法都有助于问题的分析。但对于一些非常规性的问题,这种系统的方法却不一定奏效,可能效率并不高。这时,需要有非常规的方法来应对。
如果让完全没有经验的人员进行测试并发现问题(我们称这个人为外行),遇到问题时,这个人可能有两种应对情形,第一种情形是束手无策,不知发现问题和解决问题都该从何入手;第二种情形是,这个外行不受任何成规的约束,提出一些天马行空的想法。因为没有专业背景的限制,这些想法可能真的不着边际,甚至扰乱了问题本身的解决,然而,这些不着边际的想法有时却能带来令人意想不到的效果,或是从全新的角度发现了问题的本质,或是找到了不同的思路和方法。相对而言,一个普通的专家因为受到许多既有方式的限制,就不太可能得出这种天马行空的点子了。外行以一种随性的方式思考,这种方式往往会带来意外的效果,因为随性的思考方式不会被规则限制。
作为测试人员,在发现和解决问题时,外行的思考方式可以成为有效的切入点。好的切入点是一个不错的开始,然而,真正的实践还是必须以专家的严谨和慎重来验证想法是否正确。像专家一样实践--我们倡导的还是一种小心求证的态度。软件测试是一个非常严谨并且以事实说话的过程,任何假设都必须通过测试实践的检验。
对测试已经有深入了解的人,想做到像外行一样思考,并非易事。测试专家往往下意识地对一个方法的技术可行性做判断,这种下意识能够高效地排除许多不可行的方式方法。但在某些时候,这种下意识却阻碍了创造性灵感的萌生。很多有意义的想法会因为可行性的判断而被扼杀在摇篮之中。像外行一样思考--追求的是一种新的方法或者角度,仅仅考虑某个问题是否可能存在或者某种方法是否能解决问题,不考虑方法是否有理论依据,也避免过多地考虑可行性。
其实,可行性是基于以往的经验做判断,但是谁也不能认定,当前不可行的方法就永远不可行。认为科学已经进步到了终点的想法早已被证明是荒谬的。遇到棘手的问题时,测试高手能够跳出既有的条条框框,像一个外行一样重新审视系统的整体。当然,我们并不认为测试高手是个外行,因为这种“不受约束”的审视其实是建立在对系统的全面深入的理解基础上的,并不是一种纯粹的盲目。在这种大胆假设的前提下,即使是高手,也必须小心地求证假设是否正确。求证过程离不开反复的实验和验证。
面对复杂系统的问题,这种“大胆假设,小心求证”的方法往往能产生神奇的效用。以下是一个真实的例子,在对一个多节点的集群电子商务进行系统测试时,发现在高并发访问的条件下,从应用服务器到数据库的连接数骤然增加,并很快到达连接数的上限。数据库连接数一旦到达上限并且没有及时释放时,新的请求会因为无法获取连接而被阻塞,在表面上看,系统的性能会表现得非常糟糕。
面对这样的问题,一般的问题分析方法可能难以在短时间内找到原因。这时候需要在现有的条件下大胆假设可能有问题的地方。“现有的条件”指的是一些表面的系统运作数据,如应用服务器日志、数据库锁信息、数据连接池信息等。根据这些信息,发现有种特定的操作一旦出现,系统的数据库连接请求会急剧增加。通常情况下,因为存在系统级别的缓存,重复的访问一般不会给系统带来重新计算的负担。然而,问题的表现是,反复的访问似乎对系统产生了明显的性能影响。
这种情况下可以大胆假设系统的缓存设置可能有问题,虽然按照正常流程安装和配置的系统不可能存在缓存的问题。基于这个假设,接着要做的是检查所有和缓存相关的配置内容。检查发现,价格模块的对象缓存并没有设置,而这个设置正常情况下应该是激活的。如果没有价格的对象缓存,那么相同的价格对象都不会被缓存在内存中,而是每次获取的时候都重新计算生成。
在电子商务系统中,价格信息是使用非常频繁的一类信息,因为缺少对象缓存,实际应用就有可能出现不断地查找数据库计算价格的情形,这会导致数据库连接被大量占用。更改设置后重新测试,验证发现使用了价格的对象缓存,数据库的连接数不再出现被异常地大量占用的情况,问题得到解决。发现了对象缓存的设置错误,进一步追寻原因,发现原来是系统安装的过程中配置脚本运行出现了异常,从而导致缓存的创建步骤并没有被执行。如此一来,整个问题的来龙去脉就非常清晰了。解决问题以后,这种看似很复杂的问题,其原因也许很简单。解决这个性能问题的关键在于假设问题的原因是缓存的设置有问题;而验证恰好证实了假设的正确。如果仅仅使用一般的问题分析方法来寻找问题的原因,这种“意外”的问题往往是非常棘手的。
“像外行一样思考,像专家一样实践”的方法是一位著名的计算机学者谈及学术研究时提出的一种方法论。软件测试虽然和学术研究有着明显的差异,但是测试过程中需要发现和解决问题的时候,这种方法论很有借鉴意义。在软件测试中,对待问题同样需要开阔的视野和严谨的求证态度。我们认为,测试专家能够在测试中发现绝大部分的问题并能够使用合理的分析方法找到解决绝大部分缺陷的方案,而高手则能够更进一步,最棘手的问题也能够有效解决。
能否以这种收放自如的思维方式应对测试中遇到问题,是高手和专家的一个重要分野。在大部分情况下,这种分野是不明显的,因为最困难的问题只会占所有问题的很小一部分,而这种问题在测试中不会很容易地暴露。然而这种问题被发现了之后,高手和专家在造诣上的差别就会显现出来。
1.3.2 工欲善其事必先利其器
对于测试工程师而言,虽然发现和解决问题才是体现其价值的事情,然而测试工程师不得不花大部分时间执行测试。
从图1-4中可以发现,对于一个普通的测试工程师来说,执行测试消耗了很大一部分时间,而常规项目 的任务把可用时间的90%都占用了,剩余可以用于提高生产效率的资源变得非常紧缺。而提高生产效率从长远来说又能降低常规项目任务占用时间的比例。
在一个水平较高的开发团队中,设计和代码实现的水平通常是比较高的。在这种团队中,测试的注意力会更多地放在验证和问题解决方面。验证是通过执行测试的方式完成的,真正运行一个场景,查看系统的反馈是否和预期吻合。对于结构复杂的系统和对软件质量要求很高的软件,需要执行多种类型的测试验证各种场景,而每种测试都可能包括大量的测试用例。例如,在电子商务系统一个新版本的开发过程中,功能测试的用例可能多达成千上万,涵盖各种正常或异常的分支场景。对于如此大量的测试用例,执行的工作量之大可想而知。
如果测试工程师的绝大部分时间都被执行所占据,那么可以用来分析解决问题的时间就相对很有限了。开发水平提高不能减小测试的工作量,那么,测试工程师通过什么方法更有效地完成测试任务呢?答案是提高测试效率。对于同一个测试人员,效率的提高有两种外在的表现,第一种方式是使用相同时间完成更多的测试用例执行;第二种方式是对于同一个或同一组测试用例,耗费的时间减少了。
相对于测试新手对测试执行的生疏,测试专家以熟练的执行更快地完成测试任务。提高技能的熟练程度,能够提高效率。通常来说,执行一个测试,需要完成一系列操作步骤,首先需要安装测试环境、准备测试数据,如果是使用自动化操作的方式执行的测试,则需要准备测试脚本或代码;接着需要开启监控测试环境的工具,然后才能开始执行测试用例;执行完毕后,需要收集必要的数据和结果,确认测试是否通过。这一系列步骤的执行效率可以随着熟练的程度得到提高。
如果要通过提高熟练程度来提升测试的执行效率,提升的空间是有限的。对于测试高手而言,进一步提高效率,考虑的方向应该是减少对测试的人工干预,让测试自动完成。在工业化的测试条件下,自动化水平的高低,在很大程度上衡量了一个测试团队的水平。测试自动化指的是通过编写程序完成执行测试用例的所有或部分步骤。自动化的优势是减少人工的干预,把团队中最宝贵的资源--人释放出来;同时,由于自动化是使用程序的方式实现的,因此可以保证每次执行自动化测试程序的条件是一致的,避免了人为因素引起的不一致,影响某些缺陷的可重现性。测试自动化把许多烦琐的步骤交给程序来完成,测试的执行对人的依赖也得到减弱,从这个角度来说,自动化可以提高测试的质量。
自动化测试工具是测试工程师提高效率的利器。对于不同的测试方法,已经有一批针对性很强的自动化工具可供使用。针对基于Java的单元测试,JUnit是最常用的测试框架。通过对JUnit进行扩展,还可以实现单元测试调度和自动结果收集等功能。功能测试的测试目标是端对端(End-to-End)的用例场景,在测试基于浏览器的网络应用程序时,常用的自动测试工具有IBM Rational Functional Tester(RFT)、Selenium、JMeter等。对于胖客户端的应用程序功能测试,IBM Rational Functional Tester也可以提供不错的支持。系统测试的过程需要模拟更复杂的用例场景,如不同行为的虚拟用户并发地访问用户界面,这种测试用例通常来说只能使用自动化测试工具来执行。
有一类性能测试工具使用结构化代码来模拟并发的用户行为。自动化测试工具构造测试代码的一种常用方法是录制操作步骤。当人工对界面进行操作时,录制程序可以完整地记录整个操作过程,录制完成后,测试程序员再对录制的内容进行标准化修改,修改后的测试代码就能够满足标准的测试场景要求。具备这种功能的系统测试工具很多,如IBM Rational Performance Tester、Borland Silk Performer、Load Runner等。
对于要记录测试页面响应时间的性能测试用例,可以使用Firebug、IBM Page Detailer等工具。对于安装测试,系统安装的过程可以使用安装测试脚本来自动完成,测试脚本同时可以验证系统安装的每个步骤结果是否正确。类似地,构建测试也有自动化构建测试工具完成构建的流程。作为一个成熟的团队,为了适应产品的特点,在使用一款自动化测试工具前,往往还得对工具进行定制,使工具更符合特定的测试需求。在测试创新的章节,我们还会讲述关于自动化工具定制开发的内容。
然而,自动化也会给团队带来额外的负担。如果要追求全局的自动化,那么一个完善的自动化测试框架必不可少,但是搭建自动化框架本身就是一个规模不小的工程。软件开发和测试方式随着技术的革新不断变化,这种变化有可能导致原有的自动化框架不再适用。另外,即使有完善的自动化测试框架,测试人员依然得完成基于自动化框架开发的测试用例,测试完成后,也还要对这些自动化执行资源进行维护。如果自动化框架实现的是部分自动化,那么执行过程中有些步骤还是需要人的参与,并不能完全脱离人工干预。可以说,没有经过深思熟虑而仓促上马的自动化执行方案,也许不但不能提高执行效率,反而会增加测试团队的负担。
脱离人工干预的程序控制在执行效率上的优越性很明显,因此在允许的条件下,一个测试团队应该有逐步实现测试自动化的目标和路线图。在初期,可以仅仅使用有针对性的自动化测试工具辅助测试,而随着自动化测试经验的积累,可以基于这些工具开发集成化的自动化测试框架。在实现提高效率目的的同时,也降低了“过度自动化”的风险。
小艾所在的测试团队,正是沿着这种方式逐步完成了手工测试到自动化测试的转变。当然,由于许多测试有着明确的需求,手工测试和人工干预不可能被自动化完全取代;而不同的测试种类,使用的自动化策略也可能完全不一样。
提高效率的方式多种多样,提高熟练程度和测试自动化是比较常见的两种方式。除了完成测试任务,提高测试的效率和质量,同样是测试工程师的一个重要任务。测试高手区别于一般测试人员的关键在于测试高手更善于运用创新,在实践中不断提高。
1.3.3 从拿来主义到创新
小艾所在的电子商务系统,从技术和功能上而言,几年来的变化非常明显。早期的版本采用标准的Java EE技术,业务逻辑是通过命令模式实现的。随后,基于服务的架构开始流行,服务的灵活性的确有利于提高系统的适应能力,于是,系统的实现开始从基于命令转变为基于服务。系统的前端的早期实现主要是基于JSP的标准界面,而随着Web 2.0的兴起,许多新的前端元素逐渐被加入到前端界面中,如Ajax,Remote Widget等技术的使用,使系统的前端可扩展性和可操作性得到很大的提升。早期的电子商务系统实现的功能比较单一,而随着社交化应用的流行,越来越多的社交元素被集成到电子商务系统中,系统的复杂性进一步提高。除了新技术的引入,电子商务系统的核心--中间件的版本同时也在不断更新,在这个过程中,计算机的硬件配置的发展也相当迅速。一个系统似乎从来就没有最终版本。
电子商务领域仅仅是个缩影,整个信息技术领域都是快速发展的,变化之快可用日新月异来形容。一名技术专家,如果不能紧跟技术发展的步伐,那么最终的命运很可能是被技术所抛弃。新的技术常常是伴随着新的业务模式的流行而产生的,除了紧跟技术,有前瞻性的技术专家还应该洞察业务模式的发展。
新技术的出现也对测试提出了新的要求。因为实现的框架变了,测试模型必须做出相应的调整,在新的框架下完成测试需求。前台技术革新了,如果继续使用原来的技术,测试的结果可能不再准确,因此有必要引入新的前台测试技术。随着中间件、系统硬件的升级,测试的基线和指标也必须重新构建。业务在变化,技术在更新,测试技术同样需要创新。
创新不是空想,它是以对现有技术和业务的清晰理解为基础的。对于测试工程师而言,一开始往往需要学习和借鉴现有的经验,如测试的流程、使用的测试技术、分析问题的方法等。随着学习和实践的深入,在特定产品中,特定的需求会逐渐显现。因为是特定的需求,技术上很可能没有现成的解决方案,这种需求就会被作为创新的目标。有了明确的目标,就可以开始寻找新的解决办法,寻找的过程就是一个创新的过程。