BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

敏捷自动化测试(2)——像用户使用软件一样享受自动化测试

| 作者 殷坤 关注 0 他的粉丝 发布于 2013年1月28日. 估计阅读时间: 1 分钟 | 智能化运维、Serverless、DevOps......2017年有哪些最新运维技术趋势?CNUTCon即将为你揭秘!

在本系列的第一篇文章“我们的测试为什么不够敏捷”中,根据实例总结出敏捷自动化的两大阻碍:“脚本维护困难”、“断言条件繁琐”。本文针对如何降低脚本维护难度分享一些实践经验。

近几年,Web技术发展势头迅猛,浏览器市场群雄争霸、各种UI组件库也如雨后春笋。现在互联网上已经很少有仅支持一种浏览器,并且不基于任何可复用的UI组件库进行开发的应用了。开发人员基于各种优秀的UI组件库(如,JQueryDojoExtJS)可以很容易的开发出功能强大、展现绚丽、兼容各种浏览器的页面(如下图):

UI组件库的广泛采用在提高开发效率的同时,也极大的提升了用户体验。基于UI组件库之所以能快速开发出功能强大的页面,是因为UI组件库可以自动生成海量、结构类似的HTML源码(如下图):

开发人员是幸福的,因为这一切对于他来说完全透明。于是只剩下自动化测试人员独自面对这样“恐怖”的页面源码。

如前文“我们的测试为什么不够敏捷”中所言,业界常见测试工具的脚本本质上还是针对页面源码的,因此原本就举步维艰的自动化测试在开发使用UI组件库之后变得雪上加霜:

  • 页面DOM结构非常复杂

导致所录制/编写脚本的复杂度变的更大、可读性变得更差。

  • UI框架的升级很可能会导致DOM结构的变化

因此即使开发人员没对代码做任何改动,测试脚本也会因为UI框架的升级变得无法回放。

  • 控件ID是自动生成的,甚至在每次刷新页面后都会变化

大部分自动化测试工具在“录制”脚本时,都会优先使用ID定位策略,自动生成的ID会导致这种关键的控件定位策略变得无效。

  • UI框架在各种浏览器下自动生成页面源码可能不完全相同

为了在不同浏览器下“看起来一样”,实际的DOM结构有时也可能是不同的,因此所录制脚本的浏览器兼容性会比较差。

技术的发展是为了让生活变得越来越轻松。从用户的角度来看确实如此:Web应用的功能覆盖范围越来越广、操作越来越方便、界面越来越美观。

为什么自动化测试人员没有感觉工作变得轻松呢?

要回答这个问题,首先要分析“用户使用软件”与“自动化测试软件”之间的一些重要差异:

  • 用户使用软件时只关注界面上能“看”到的,而不用总是“查看页面源码”;
  • 用户会更关注整体业务的正确性、稳定性,而不仅仅是每个孤立页面功能的正确性;
  • 用户对页面样式、响应时间、浏览器兼容性要求越来越高; 如果我们能像用户使用软件一样进行自动化测试,测试就会变得更敏捷:
  • 根据界面快速编写测试脚本:敏捷应对需求的变化;
  • 降低对技术实现(UI框架、页面样式/布局)的依赖:敏捷应对设计/开发的变化;
  • 测试脚本可以稳定支持各种浏览器:敏捷应对环境的变化;

(一)根据界面快速编写测试脚本的实现思路

还是以前文“我们的测试为什么不够敏捷”中用到的“用户增加”界面为例:

如果你是作为用户在使用上述功能时,心里一定也会默念下面内容吧:

“账号”输入***、“密码”输入***、“姓名”输入***、“性别”选择***、“生日”输入***、“国籍”选择***,点击“保存”按钮。

这就可以理解为“根据界面编写的测试脚本”。

我们在使用Web应用时,心里常常默念的还包括:“展开/收拢/选中”树(Tree)的某个节点、数据表格(Grid)的下一页/上一页、选中数据表格(Grid)的某一行、点击/关闭某个Tab页,等等。

很多人在与笔者交流的过程中都会问“为什么还要手工编写脚本,怎么不提供脚本录制工具呢?”

因此在这里想澄清一下大家对“编写”脚本的误解,与传统自动化测试工具相比,“此脚本”非“彼脚本”。

传统的测试脚本是给特定测试工具执行时使用的,对人类而言可读性极差,更别谈手工编写了,因此“录制/回放”工具往往都是必备组件。即便如此,此类“录制/回放”工具的实际使用效果,也是不尽如人意的。

从这种角度来看,“录制”脚本是“下策”,“上策”应该是让测试脚本大幅简化,并且具备良好的可读性和可维护性,以达到人工编写很容易的程度。

——以上只是笔者的一点差异化见解,对业界优秀的测试工具没有任何冒犯之意!

(二)降低测试脚本对技术实现依赖度的实现思路

Web页面开发中的技术实现细节主要包括UI框架的选择及版本升级、页面样式设计、页面布局设计,因此我们的主要目标就是降低测试脚本对这些因素的依赖。

为了便于测试脚本的解析和组织管理,目前还不能直接写形如“点击(保存)按钮”这样的纯自然语言,但可以设计成接近自然语言的领域专用语言(DSL:Domain Specific Language),本文采用XML来实现这种DSL。比如:

<event id="[button]保存" name="click"/>

这种超级清晰、简短的测试脚本与实际海量的页面源码之间有一条鸿沟,我们可以通过“封装”来屏蔽。下面以ExtJS的Button控件为例来示意如何完成这种封装:

  • Button的界面展现如下:

  • 实际生成的页面源码DOM结构如下(省略部分非关键属性):
<div id="button-1032" class="x-btn x-box-item x-btn-default-small 
        x-noicon x-btn-noicon >
    <em id="button-1032-btnWrap">
        <button id="button-1032-btnEl" class="x-btn-center"
                 autocomplete="off" role="button">
            <span id="button-1032-btnInnerEl">Cancel
            <span id="button-1032-btnIconEl" class="x-btn-icon "/>
        </button>
    </em>
</div>

我们封装所要做的主要工作就是解析出测试脚本中定义的关键信息(button、保存、click),结合对应页面源码的DOM结构,翻译成W3C标准的定位方式(如,XPath)。通过XPath就可以定位到页面上的任何控件。

对于测试脚本(<event id="[button]Cancel" name="click"/>),翻译后的XPath可以是(//button/span[text()='Cancel'])。

同理,其它测试脚本还可以包括:

  • 树节点展开/关闭
<event id="[treeNode]部门" name="open"/>
<event id="[treeNode]部门" name="close"/>
  • Grid翻页
<event id="[grid]人员列表" name="nextPage"/>

它们的封装实现比Button稍微复杂一些,但原理是一样的。

(三)增强测试脚本浏览器兼容性的实现思路

这个就比较简单了,只要选用标准化程度高、厂商支持度高、扩展性强的第三方底层实现即可,笔者推荐采用Selenium WebDriver

通过上面的介绍,有些读者或许会有个疑问:“如果一个Web应用没有采用任何UI框架、而且编码又极为不规范,那么你这种方案岂不就不适用了?”

答案是肯定的。但笔者认为此类Web应用如果想要发展下去,其瓶颈还停留在开发环节,对其进行自动化回归测试的投入产出比不是很大。

此外,为了进一步提高Web应用的可测试性,笔者在实际工作中总结了一些前台页面开发的最佳实践,供大家参考:

  • 页面采用单帧实现

如果页面上的frame/iframe嵌套很多的话,控件的定位会比较麻烦,并且影响展现速度。

  • 兼容Firefox

Firefox有很多强大插件,如Firebug、FirePath,非常有助于在开发、测试过程中的调试问题。

  • 采用统一的UI框架开发复杂控件

对于复杂控件,比如Tree、Grid,如果不基于统一的UI框架实现,开发、测试工作量都会很大。

  • 对于需要设置ID的控件,一定要设置唯一、并且有业务意义的ID

当然对于一般不需要设置ID的控件(如,div)或者控件ID由UI框架自动生成的情况除外。

  • 对于Form中最常见的label+input组合,尽量让label和input有逻辑对应关系

推荐为label设置for属性,属性值等于input的id,这样对最终用户也比较友好:双击label的时候,鼠标焦点能定位到对应的input中。

  • 页面设计时考虑用户体验,往往用户使用方便的时候,自动化测试也会方便

比如,一个页面上不要有多个同名的按钮、通过点击上下微调按钮(箭头)改变输入域值的控件也支持直接输入值、日期选择控件支持用户直接输入、下拉列表控件支持输入过滤,等等

对以上几点最佳实践有如下补充说明:

  1. 这些最佳实践不仅能提高Web UI的可测试性,也非常有助于提升用户体验;
  2. 这些最佳实践如果能满足更好,即使不能全部满足,也可以开展自动化测试;

按照本文给出的方案,前文“我们的测试为什么不够敏捷”中用到的“用户管理(增加、删除)”功能的自动化测试脚本就可以改造为如下样式(示意代码):

  1. <event id="[button]新增" name="click"/>
  2. <event id="[input]帐号" name="setValue" value="${account}"/>
  3. <event id="[input]密码" name="setValue" value="1"/>
  4. <event id="[input]姓名" name="setValue" value="${accountName}"/>
  5. <event id="[input]性别" name="select" value="女"/>
  6. <event id="[input]生日" name="setDate" value="1980-01-01"/>
  7. <event id="[input ]国籍" name="select" value="中国"/>
  8. <event id="[button]保存" name="click"/>
  9. <event id="[gridRow]${account}" name="assertExist"/>
  10. <event id="[gridRow]${account}" name="check"/>
  11. <event id="[button]删除" name="click"/>
  12. <event id="[ gridRow]${ account }" name="assertNotExist"/>

以上测试脚本完全基于界面上“可见”的内容进行编写,大家应该不需要看下面的解读,也能明白脚本的意思:

  • 第1、8、11行表示点击“新增”、“保存”、“删除”按钮;
  • 第2~7行表示为输入域赋值,赋值方式有输入文本、输入日期、选择下拉选项;
  • 第10行表示选中表格中的指定行,即,选中指定行前面的Radio按钮;
  • 第9、12行表示断言表格中指定的行“存在”或“不存在”;
  • ${ account }表示使用外部的变量account;

自动化测试中最关键的两部分是“脚本”和“断言”。至此,自动化测试脚本的编写、维护以及执行已经可以跟上敏捷开发的步伐了。本系列的下一篇文章会就“如何让断言不再成为自动化测试的负担”这个话题来分享一些实践经验,敬请关注!

作者简介

殷坤,东软集团资深测试经理、技术讲师,10年软件研发、实施、测试及项目管理工作经验。 目前专注于敏捷项目管理及质量控制、过程改善、自动化测试、持续集成、用户体验提升等方面。


感谢侯伯薇对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

关键字驱动?? by 郑 鸿志

<event id="[treeNode]部门" name="open"/>

这种形式的脚本描述到selenium可以执行的代码之间是需要转换解释的,你的意识就是弄一套自定义的关键字,然后有一套类库去解释这些关键字吗?

Re: 关键字驱动?? by 郑 鸿志

这种用xml描述的方式,我在玩flexmonkium的时候看到过,比如这样的描述:<UIEvent command="Click" prop="automationName" value="交易管理" />

Re: 关键字驱动?? by yin kun

“静态”的看像是关键字驱动,“动态”执行的时候,最好是实现成测试数据驱动。

Re: 关键字驱动?? by yin kun

“设计”的关键无非就是“抽象”和“封装”,对应到此处就是对用户操作的抽象和对Selenium的封装。
万变不离其宗!

Re: 关键字驱动?? by 郑 鸿志

框架代码是不是要实现一些解释器的功能呢?

Re: 关键字驱动?? by yin kun

是的,但这只是框架中很少的一部分功能。
测试是与开发、构建、运维等紧密关联的一个系统性活动,其中自动化测试也仅仅用于“执行”环节。
用例/脚本的如何更有效、什么样的测试报告能更准确的定位缺陷、测试数据如何更灵活、覆盖度如何度量、“关键字”驱动对于国际化问题怎么处理、如何让测试脚本发挥更大的价值(比如,除了回归测试,还可否应用到客户端性能、兼容性、稳定性等方面),甚至如何让研发团队充分配合,都是“框架”该考虑的事情。

Re: 关键字驱动?? by 郑 鸿志

你说的这些问题是怎么解决的呢?
解释器支持循环分支逻辑吗?

Re: 关键字驱动?? by yin kun

在解释器里支持对基于一组参数(测试数据)循环驱动同一个测试脚本,是最基本的要求。
往往在生产环境测试脚本所需的参数不是简单循环就可以的,可能还需要指定分组、可能还需要动态赋值。
此外,一个“框架”如果想要被广泛接受并运转良好,除了提供一些列工具(特性)之外,还要辅以如何使用这些工具的最佳实践、培训以及方法论,并预留扩展点。
这些超出这篇文章的分享范围,以后有机会可以考虑再单独撰文分享。

Re: 关键字驱动?? by 郑 鸿志

大侠有博客吗?或者有啥联系方式,请跟您讨教。

我们公司也是这种思路 by 贾 延平

我们公司也是这种思路。我们针对GUI程序开发一套测试框架,完全按照用户可见的角度来进行控件的定位和操作.
我们从08年就开始做了,还算是比较成功的。现在有些产品的测试脚本代码量都赶上程序源码了。
测试人员多次要求加录制脚本的要求,但是我们考虑录制脚本可能导致不可维护的脚本,所以一直没有提供。

我们的脚本是用ruby做的,按马丁服了的说法是“内部DSL”,例如:
button('确定').click

Re: 关键字驱动?? by yin kun

很抱歉,我没有博客和微博,更没有QQ空间(哈哈)。
工作邮箱不便公开,私人邮箱为:yinkun@dl.cn,回复如有不及时,望见谅。
相关联的问题也可以在这里讨论,其他人也能够分享或拍砖

Re: 我们公司也是这种思路 by yin kun

1、“现在有些产品的测试脚本代码量都赶上程序源码了”,
这个有些不可思议啊,用户使用的路径不太可能像程序源码那么多。
斗胆揣测一下:是否你们的“测试数据”没有实现参数化(直接耦合在“测试脚本”里)?或者测试脚本的组织和复用做的不够灵活啊?我以前也尝试用JRuby实现这种DSL,就发现测试脚本的组织和复用不太方便,后来改成XML格式。
2、“测试人员多次要求加录制脚本的要求,但是我们考虑录制脚本可能导致不可维护的脚本,所以一直没有提供。”
我也一直认为“录制”是“下策”,我这边考虑后续提供一个测试脚本辅助编辑器,在上面可以选择指定页面的“可见”控件(比如,姓名、性别、保存等),及其对应的操作(比如,赋值、点击等)。

Re: 我们公司也是这种思路 by 郑 鸿志

这种框架有一个问题啊,解释代码是专用的,只能针对你们公司制定的开发规范,不具有普遍的指导意义。

Re: 我们公司也是这种思路 by yin kun

考虑到了你说的情况:
解释代码确实是专用的,但针对脚本的语法接口是通用的,针对不同的开发规范或UI框架可以做不同的实现。
我们公司不同部门项目的开发规范和UI框架也是有差异的,但只要做不同的语法接口实现即可(如果开发规范变更或UI框架升级也只需要局部修改解释代码)。
一套解释代码的实现是很容易的,毕竟相对整个测试框架来说,一套解释代码的代码量是很少的。

Re: 我们公司也是这种思路 by yin kun

而且,一套脚本解释代码的实现复用范围不是开发规范级别的,而是UI框架级别的。比如,我们做了JQuery、Dojo、ExtJS的实现,这种复用范围理论是和UI框架的复用范围一样广泛。

框架的开发不轻松 by 苏 志茹

第(一)和第(二)是让脚本变得更可读, 如果测试人员只负责编写和维护脚本, 那门槛是变低了。系统页面改变,依然需要“必须不断的修改才能在最新版本上跑通”, 但脚本和页面源码耦合少了, 脚本维护变得轻松? 但是如果中间的封装代码的开发和框架的维护也是测试人员来做的话, 感觉依然是繁重的, 至少在框架的开发阶段需要投入较大的人力, 门槛很高。 而且不是很确定这种框架是否能一劳永逸。 至于第(三)点, 这种编码规范, 无论是用那种方式实现UI的自动化测试, 都是必要的。

其实和敏捷没什么关系 by 苏 志茹

建议这一系列文章标题改名为 “UI自动化测试”, 这样比“敏捷自动化测试”更清晰。

Re: 其实和敏捷没什么关系 by yin kun

谢谢您的建议,本系列文章前几篇确实是面向UI自动化测试的。
之所以定名为“敏捷自动化测试”,是因为敏捷开发必须要自动化测试,但自动化测试本身的敏捷性不足。
所以希望通过本系列文章分析一些相对“敏捷的”自动化测试方案。
再次感谢您的建议。

Re: 框架的开发不轻松 by yin kun

1、如果页面发生功能性的变换,测试脚本当然是需要修改的(同样研发也是需要修改代码的)。但如果页面仅是样式或布局调整,测试脚本一般不需要修改。
2、完整测试框架(语法定义、执行、断言、报告、覆盖度等等)开发不是一朝一夕的,但基于框架封装测试脚本语法实现的工作量非常可控。

另外,我个人比较倾向于测试团队负责框架开发、维护工作。测试人员会比研发人员投入更高的热情,同时测试人员的能力也会明显提升(没有什么比这更重要了)。
当然,测试框架开发的ROI,最终还是取决于它的复用范围。

如果要校验数据库怎么办呢? by 郑 鸿志

如题

Re: 如果要校验数据库怎么办呢? by yin kun

一言难尽,我后面的文章会分享关于校验(断言)的一些经验。
敬请关注!

没啥真正有用的内容啊 by s l

现成的UI框架对于研发是一种快速实现并且可以降低一些低级程序员的技术能力限制的工具,对于研发是好的,而对于自动化测试则恰恰相反,研发使用可复用的UI框架越多,对于自动化测试越困难,失效也越快。
脚本写成这个样子有什么用么,看不到这种脚本有任何的好处啊,这样组织脚本有什么必须成这样的原因么,这种脚本已经不是变成语言了,这种翻译式的脚本,还得自己写一套翻译的工具去解析,这个工作量有这个必要么,而且像if else、while这样的控制结构怎么弄,难道在xml里面再定义这些结构?那还不如直接用现成的编程语言呢,非得折腾这个干啥,实在想不通。

自动化测试不够敏捷的原因我觉得是人们把自动化测试专注于UI层上,这个是自动化测试失败的罪魁祸首,自动化测试永远都是自底而上的,自上而下的自动化测试没有用,白忙活,UI层就手动就行了,用不着整这些花哨的东西,一点儿用都没有

Re: 没啥真正有用的内容啊 by Wang Jensen

有同感,看了作者2篇关于敏捷自动化测试的文章,感觉是发现了问题,但是对于如何解决,并不是说的很具体。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

23 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT