自动化测试能否帮助我们我们提升开发效率,关键在于其有效性。如果其有效性可能存在问题,那么可能是什么导致了这种问题的产生呢?对自动化测试产生作用的方式存在误解,对自动化测试能够产生作用所要求的条件存在误解,自动化测试分析设计的随意性,自动化测试开发维护的低标准,对自动化测试资产的低准出条件……本文将就自动化测试有效性简单阐述我自己的一点见解,抛砖引玉。
观念之一:独木难生于漠,密植方育甘霖
沙漠中间栽下一棵树,枯死只是迟早之事;即便有足够的资源让它能够永久地生存下去,而它除了给路过的摄影师的构图上增添一分绿色气息,便再也没有其它存在的意义了。如果要想它能够长久而有生命力地活下去,并期望它能够改善生态,那就需要将其根植在一片密林之中。自动化测试,尤其是前端自动化测试,如若离开其他层次的自动化测试和技术手段与之相互配合,便会如同这棵沙漠中间的树一样,不久便灭。
五一节后的周五下午和两个开发经理一起review一个项目的性能测试需求,休息闲聊时顺道提到了其中一位开发经理所负责的公网系统CI连续飘红的问题。他坦言自己对自动化测试是不信任的,认为自动化测试发现不了任何问题,从而对CI带来的帮助表示怀疑,因此不愿意在CI上投入过多的精力。我理解这位经理的感慨,认同他对自动化测试有效性的担忧,但是这并不能使我认同他对CI和自动化测试的态度。而对于软件开发来说,没有自动化测试、没有CI,我们可能无法期待更高效率的开发和更好的质量保证。但他这是因为没有耐心地植下一片树林,才导致他心目中自动化测试这棵大树的死亡,进而却又否认这片树林存在的意义,是不妥的。
观念之二:降低期望于心,提升目标于行
为什么有很多人对自动化测试的可信任程度表示怀疑呢?这源于一句看似真理的废话:自动化不是用来发现缺陷的,而是为了验证系统改动是否造成关联影响并用以增加质量信心的。这话本意无非就是想告诉大家:不要对自动化测试的结果抱太高期望,对机器智能和人脑的差距要认清。这个本意本无可厚非,但是这句话现在却逐渐成了自动化测试低质量的借口了。
首先,很多人认为自动化测试无法发现缺陷是因为测试脚本无法代替人类的自主思考,无法灵活变通。而我们之所以期望在测试执行时能够灵活变通,是因为测试分析设计时对被测应用设计细节的不确定性。非探索式的脚本化测试设计就是要通过输入精确的操作和数据类型,从而得到精确的输出来和预期结果进行对比,在这种模式下,没什么问题是需要变通才能被发现的。
其次,不得不说自动化测试分析设计的难点在于对输入条件分支和状态机组合的穷举和选择上,这需要很大的成本。不幸的是,通常在做自动化测试分析设计时,我们习惯于用经验和主观感觉去做,不全面和主次不分的情况常常出现。因而,自动化测试分析和设计的不严谨、不完整也是自动化测试不能被信任的原因之一。
此外,自动化在那些已覆盖的测试上面表现的也不尽如人意,笔者观察到,大多数人并不是用自动化测试脚本去做测试或质量守护工作,而只是用来让他们运行通过,以获得KPI的达成和成就感。自动化测试运行失败,不习惯于或不乐于使用它的同事就会很轻易忽略潜在的问题,所以漏测和封版延期是常有的事。
最后打个比方,自动化测试就像是看门的狗,没有它,家里进贼未必发现不了,关键是发现的时候是否还来得及挽回损失,而狗狗能不能在有贼光顾的时候示警,取决于我们如何驯养它。我们期望家里进贼的时候狗狗能够示警,但并不会期望它能够帮我们直接把小贼擒住;而且并非每次狗狗示警都意味着有贼入室,也有可能是阁下经过而已。自动化测试与此类似,凡其所致,但有一点不一致便会得到失败的结果,而至于是不是发现了虫子却又是另外一码事,想用自动化帮你解决所有问题,那你还是洗洗睡吧。
总之,我们对自动化测试有什么样的期望,就应该有什么样的目标和要求;设定了什么层次的测试目标,就应该有什么样的使用效果。如果告诉你自动化测试可以朝着任意品种的狗狗去培养,目标就是看家护院,你非要以哈士奇食量大喂不起为由非要选小泰迪,那能有什么办法呢?如果自动化测试分析设计不严谨,而已覆盖的脚本也没有严格编写,而或脚本严格编写却并未善加利用,那么自动化测试自然就是一个彻底的谎言了。
我们可以期望用自动化测试来暴露所有的问题供使用者抉择,而非用来精确定位每一个问题。即便我们不期望自动化测试发现所有的BUG,但是我们必须要用发现每一个BUG的标准去分析设计和利用我们的自动化测试用例。
方法之一:善用手动测试用例库
如果有完整或近乎完整的手动测试用例库,在做自动化测试的时候用它来甄别自动化测试范围、做自动化测试需求分析和设计是非常棒的,可达事半功倍之效。因为除了SLA签订的关键功能清单,我们可以分析测试用例库中最近X个月、Y个版本中被执行的测试用例的频率和比例等等数字,从而很容易得出最有价值做自动化测试的范围,而如有其他因素也可以加入综合评估。这时候,关联或映射手动测试用例和自动化测试脚本可以很容易地衡量自动化测试的覆盖率等相关指标。
除此之外,手动测试用例之于自动化测试开发的意义还在于一种特定模式下的自动化测试开发:测试人员按照一种特定的规约编写产品或项目的手动测试用例,随后用一种模型化的转换规则去生成自动化测试脚本,继而再去修改和优化。这种模式的好处是,自动化测试脚本的开发者不需要懂业务知识,只需要按照业务测试人员编写的详尽的测试用例去实施。而不足之处却也十分明显,由于思维方式上的差异,编写测试脚本过程中的沟通成本不会太低,而且对建模水平要求很高。
方法之二:搭建适应性强的框架
开源测试工具,我们组先后用过Selenium RC、WebDriver、WatiJ这几种工具模式,框架采用JUnit和TestNG,测试调度和CI使用Jenkins管理。做起来最大的感受就是,像WebDriver这种工具,它本质上是为互联网而生;如果要用于我们的企业ERP应用的自动化测试,如果没有做一系列适应性的API和组件封装,测试脚本开发对于大部分同事来说都是件头疼的事情。
经过摸索,我们结合PAFA(PingAn Foundation Architecture)架构的前端页面特征做了对象识别、页面加载和处理缓冲、运行监听、数据处理、断言和运行容错、报告、日志以及第三方组件(如Autoit、Cmd等)兼容的封装操作。这种封装属于Bot-Style,适用于我们基于业务操作驱动的自动化测试用例编写,能够大大地降低我们测试脚本开发的技术成本。虽然它们看起来运行起来效率并不是十分高,但是对于被测应用的响应速度来说已经是数十倍的效率胜出了。至于使用Page Object还是Bot-Style,还请大师们不要吐我的槽,这没什么可争论的,因为这只是面向前端业务操作的页面测试,二者的表现毫无优劣之分,完全在于队员的测试脚本编写风格偏好。
附图1:测试中的TestCase样例
附图2:测试中的TestApi样例
我们正在从瀑布开发模式下逐渐开展持续集成和敏捷转型,不难预见,达到一定成熟度之后我们大部分团队会使用ATDD或BDD模式。这种快速开发的基于前端的自动化验证测试虽然在验证测试中所占的比例会越来越小,但它们将依然有用武之地,同时对当下的系统前端的回归测试也可以完全支持。
自动化测试框架强大与否主要在于其适应性,所以与其说自动化测试框架是选择出来的,不如说它是被改造出来的。产品比较单一的小公司或者开发框架比较成熟的公司用一个工具、一个框架在一个测试平台下就可以解决一个公司的自动化测试实施的绝大多数问题,而大型企业的应用繁多复杂,很难由一个简单工具或者框架来解决。
自动化测试框架,没有自己DIY过的永远都不要相信不造轮子这种说法,因为只有在你做的时候,你才能够更深入地理解你的工具、框架和你的被测应用及其所依赖的开发框架的特征,才会产出更具适应性的测试框架和特征库。
方法之三:善用设计与重构
无论是前端自动化测试还是单元测试,脚本代码结构的重要性是个老生长谈,因为这关乎测试脚本代码的可读性、维护经济性等问题。由于我们的业务系统的测试案例主要是基于操作流程的业务场景,所以我简单说一下我们在这方面的做法与经验,而单个功能点特性的测试分析也基本与此类同,不再独占篇幅。
测试范围与设计
大型企业,尤其是金融行业的ERP应用,很多系统实际上都是规则零散、流程复杂,规则引擎应用得并不是很充分。这便是前文所提及的自动化测试分析设计时对输入条件分支和状态机组合的穷举和选择困难的问题的产生原因了。对于这种情况,我们可以借鉴两个基础的测试设计方法:正交覆盖和Pairwise/All-Pairs,其基本原理和用法这里不再赘述。
以正交覆盖为例,我们需要把影响流程分支走向的因子罗列出来。一般理论认为,缺陷的产生绝大多数是因为2个因素组合产生的,所以我们先做一个正交强度为2的配对来确认最小的测试组合范围。然后将正交强度变为最大,得到一个最大的测试组合范围,重新审视强度为2时的子集之外的其他案例是否有必测的理由。注:正交强度最大的组合结果集并不是简单的笛卡尔乘积,因为组合条件之间可能存在一些逻辑限制;此外,鉴于篇幅有限,本文就不再罗列下表最终正交得出的测试组合结果。
图3:保险业务操作伪场景分析正交表样例
通过对前端业务操作流程的分析,可以建立完整的前端的测试用例库,当然,前端的自动化测试可能只会选择其中的一部分去做,如上文所述,自动化测试覆盖率也可以很轻易地度量出来。在设计评审过程中,流程设计完成者需要向评审人员展示各个业务流程的测试组合结果,并且解释通过怎样的组合方法得到这些结果;进一步阐述依据什么样的原则得出自动化测试范围的选择结果。
特性组件抽取
完成了对测试案例场景的界定,我们便已明确我们要在被测应用上做什么样的操作,接下来的工作就是一些通用的简易特性抽取。虽然很多人声称复用不是个好东西,但它在编写测试脚本中适度的使用,却也可以很好地减少测试脚本开发和维护成本。
第一层的特性抽取可以通过测试框架和工具来完成,简易的二次封装是保证测试书写低复杂度和保证测试脚本健壮性的好办法,就如同图中对click方法的简单封装一样。这件事情只需要一个像笔者一样略微熟悉测试工具和被测应用特性的测试工程师即可完成。所以这个活动的成本不会太高,而带来的效益却还是很不错的,至少可以给不熟悉测试工具的脚本开发者一段很长的学习缓冲期。
图4:STAR测试框架中的Api样例
接下来就是被测对象的共有特性组件做抽取,仍以前端自动化测试脚本开发为例,我们可以参照被测应用本身的组件复用规律来提取测试脚本中的公用页面组件。例如,图3所涉及的保全操作流程中,至少包含保全申请发起、质检、核保这三个可公用的页面组件。假如最终需要测试的场景案例有8个,其中5个必须质检、4个必须人工核保,那么在这三个点上就可以节省14(8+5+4-3)个测试Api的维护和开发成本。
共有特性组件既不能设计的过于粗糙,从而造成在不同的自动化测试脚本中无法统一支持;又不能过度抽象,进而导致可读和可维护性变差。目标很简单:任意一处纯前端页面的改动,我们只需要修改一个测试Api;任意一个分支场景的规则改动,我们只需要修改一个测试案例脚本的数据和验证方法。
脚本重构与舍弃
虽然测试脚本应该被良好地规划设计,但实际执行的时候可能会遇到一些问题,比如在识别公用特性组建时存在遗漏却未在评审环节被发现,抑或被测应用的代码重构甚至是流程再造同样会要求我们去做这些对应的测试脚本重构的工作。
我每天都会从SVN上checkout我们组17个工程的测试脚本,之前经常会发现某些系统一次性update十数个甚至数十个文件。这时候我会问一声为什么,最初得到的答复都是由于系统页面发生变动,所以这部分测试脚本都需要同步修改。我的回复一律都是:重构,重新做公用组件抽取,立刻做,因为我们耗不起这个维护成本!几次之后,再遇到这种情况,得到的答复便基本上都是:因为XX的变更影响了一些模块的测试脚本,所以我重构了一下。这时候我的心里就比较嗨皮,比较有成就感了,因此我也乐于不断地给大家灌输这种从别处学来的理念,而测试脚本重构的动力也从单纯的前端页面变更驱动变得更加多样化。
伴随着测试脚本的开发,债务不可避免的在不断产生,如果不加以管控,终有一天我们都会由于对之无法负担而崩溃。所以我们如果真的重视自动化测试,我们就需要不断地优化我们测试脚本的结构,让它始终停留在我们可以控制的范围之内。必要的时候我们可以放弃一些已经开发完成的但是优先级稍低的测试脚本,即便它们运行得很好。在负担能力满载的情况下,以放弃低优先级的部分为代价来换回我们对测试资产持续优化和维护的能力、换回我们对这种低端技术债务的负担能力,始终都比承担不可控甚至崩溃的风险要来得更加有价值。
方法之四:坚持定期脚本审查
在团队里面推广自动化测试应用,同样也会存在很多沟通和理解上的误差,加之不是所有人的技能都在一个层次上,所以大家可能对测试脚本开发的各种要求的理解上都会存在不同的误区。因此,测试脚本开发完成之后第一时间要进行审查,这是测试脚本有效性验证的第一道关,虽然这个活动将耗费不少时间。
测试脚本审查的手段分两种:工具检查和评审会议。一般建议在召开评审会议之前使用代码扫描工具如sonar或者checkstyle、 findbugs等,定义一些通用的规则去检查测试脚本书写和设计结构上的问题。使用工具的方法本文不介绍,完成这些基础不合规项的检查和修复之后则可以开始后续的测试有效性评审。测试有效性评审是个技术活也是个体力活,不仅需要很强大的业务知识支撑和对系统设计模式甚至架构的理解,同时也需要足够的时间和耐心来从事这项活动。接下来,简单说一些实际的检查点,通过这些检查点,我们将识别一段测试脚本是否能够达成测试目标,是否经济合理。
- 经过设计评审的每一个案例是否都已经完成开发,是否存在额外的新增覆盖,为什么;
- 预设的公用组件是否已经完全抽离实现,对于假设的被测应用修改,能否用最简单、最经济的测试脚本修改来支持同步;
- 测试数据的初始化和使用是否合理,是否会破坏测试环境中数据的健康度甚至带来环境故障;
- 测试的验证点是否精准足够,是否存在反向的验证方式(如,只检查是否有错误发生);
- 测试脚本中是否存在多余的结构体,是否存在非预设的随机性分支;
- ……
子曰:脚本三月不复查,CI即便全蓝——虫子照样遍地爬!自动化的测试有效性本就是需要通过维护去保持的,定期重新审查本就是一种测试脚本的维护手段,是非常有必要的。而频率则需要结合团队对测试脚本维护的力度来看,复审的主要的方法与开发完成之后的初次审查是一样的,只是关注点会稍有不同。
两句废话做总结
自动化测试是达成目标的工具手段还是负担,关键在于它对我们产生了什么样的影响,要产生好的效用,就必须保证其测试有效性。保证自动化测试有效性的手段很多,笔者目前的水平只能说到这里,但愿对入门者和彷徨者有所助益,高手和喷子们便一笑而过便可。