首先,页面对象的识别问题。这是任何一种基于UI实现回归测试自动化的框架都会遇到的问题。在C/S时代,已经出现了很多定制化UI组件难以被工具识别的问题,可好歹总逃不出windows的手心。到了web时代,前端实现技术百花齐放,而前端代码的编写也更是一个开发人员一个习惯,如果缺少统一的编码规范,对于那些没有使用id或者name之类属性的页面对象,传统的关键字驱动框架没有例子中看起来那么美好了。当然,有人说xpath可以解决一切问题。嗯,xpath是很强,但是一个没有开发经验的tester想掌握它其实也不会比学会一点基本的编码技术容易多少;另外,xpath并不是的,在实际中还是有些它处理不了的情况。下面再给个例子(如果tester看不懂这个例子,估计学会xpath也有点困难):

  上面这个图中是一个表格,id列(第一列)和需求名称列(第二列)下显示的内容,都是可以点击的超链接,后的“操作”一列中的一个个小图标也各自指向一个链接(分别是“变更”、“评审”、“编辑”、“建用例”),通过查看第一行记录的html代码,我们发现:

  假如想要编辑一条记录,没有可利用的id或name属性,可用的是link;

  link指向的url中包含了这条记录的ID信息,因为这里是第一行记录的代码,所以都是341,而后面的每一行记录的代码都是各自的ID。

  上面这个例子是企业应用中常见的场景,关键问题在于数据记录ID是一个不可控因素,而数据记录的title/name之类的是可控的,为了提高test case的可维护性和相互独立,我们肯定不会依赖ID去模拟页面操作,而是根据title/name之类取回ID信息,再拼装回所需要操作的链接。这种情况下,xpath恐怕也搞不定了。(如果这里再涉及到要在几个iframe之间跳来跳去,恐怕写脚本的人要崩溃了。)

  当然,有人会说关键字驱动框架一般也可以定义function来实现类似的操作啊。唉,都到了编写自定义function的地步了,干嘛还非要纠缠在关键字驱动上啊。

  第二,脚本的可维护性问题。如一开始的例子,传统的关键字驱动是一种纯“面向过程”的脚本组织方式(像C/pascal),表格中填写的是一个操作序列。如果多个test case 都涉及到某个页面,基本上会在多个case中都看到类似 clickAndWait btnG这样的内容,而一旦页面中btnG改名叫buttonG了,或者 clickAndWait btnG与verifyTextPresent 之间增加了一个 clickAndWait XXX的step,那基本上每个case都需要修改。

  嗯,问题来了,当你的脚本数量从1增长到100、1000的时候,当UI的变动无法避免的时候,当你发现100个case回归执行只有90个通过,执行失败的10个需要逐个检查错误日志和查看截图,再挨个修改的时候,当下次回归测试又发生这种问题的时候——基本上这是一个死循环了。如果解决不了根本问题,前期投资可以舍弃了,别纠结了。

  当然,有人说关键字驱动已经进化了,可以跟新的webdriver结合起来,提升关键字的封装层次,解决这个可维护性的问题。好吧,这个问题等下再详细说。

  第三,脚本的可扩展性问题。在大规模实施自动化的过程中,脚本一般都会简单的是一行行的browser.find_elmenet_by_id(xxxx).click() 这样的模式,根据各种条件来判断执行的分支,进行各种异常处理,第三方类库/包的调用,主机环境的访问,诸如此类,这些对于所有的3GL/4GL来说其实都很容易实现,但对于传统的关键字驱动来说,嗯,也可以实现,大概是下面这个样子【摘自robot framework(此robot非rational robot)】:

  下面是一个在 keyword 表格里面实现的 FOR 循环:

:FOR

${var}

IN

@{SOME LIST}

 

Run Keyword If

'${var}' == 'EXIT'

Exit For Loop

 

Do Something

${var}

 

  这是获取时间,当然也是写在表格里面的:

${time} =

Get Time

 

 

 

${secs} =

Get Time

epoch

 

 

${year} =

Get Time

return year

 

 

${yyyy}

${mm}

${dd} =

Get Time

year,month,day

@{time} =

Get Time

year month day hour min sec

 

 

${y}

${s} =

Get Time

seconds and year

 

  import 外部的库:

Import Library

MyLibrary

 

 

 

Import Library

${CURDIR}/Library.py

some

args

 

Import Library

${CURDIR}/../libs/Lib.java

arg

WITH NAME

JavaLib

  读取外部文件:

Import Resource

${CURDIR}/resource.txt

Import Resource

${CURDIR}/../resources/resource.html

  正则表达式:

${escaped} =

Regexp Escape

${original}

@{strings} =

Regexp Escape

@{strings}