在开发自动化脚本的过程中,经常会碰到由于页面元素布局发生微小改动、添加或者删除控件,导致整套脚本无法顺利运行,同时脚本的维护工作也由于脚本需要重写或者重新录制而变得繁重。实际上这种问题出现的原因是我们把对象的识别限制在针对某一具体控件上,例如一个文本输入框。本文将阐述利用 IBM automation framework 来实现在更大范围内对象识别的机制,并利用这套机制解决上述问题。
我们将对象识别范围扩大到表格 (table),在表格中动态查找控件,那么上述问题将得到缓解。如果表格也发生变动,我们只要将表格重新抓取,从而以微小的代价实现脚本无需变动,依然能够运行。
IBM GUI automation framework 由三部分组成,分别是三层体系结构 (appobjects,tasks 和 testcases)、ibm package 和最佳实践。三层体系结构的目的是为了将“做什么”和“怎么做”分开,从而做到代码重用和接口一致的对象识别机制。appobjects 层是用来存储 GUI 元素的地方,通过调用该层的 getter 方法返回 object,用来查询和操作 GUI 元素;tasks 层是构建可重用公共方法的地方,也是用来存储查询和操纵与应用程序相关的复杂控件的地方;testcases 层调用 tasks 层存在的方法,模拟应用程序的使用,验证状态和结果。ibm package 是提取出来的跟应用程序无关的代码,包含开发自动化脚本过程中需要用到的多种常用功能,供 IBM 所有测试 team 使用,能够大量节省开发 automation 脚本的工作量。
(查看大图)
页面上的常用控件包括文本输入框、单选框、复选框、下拉框、链接和按钮。我们针对不同控件类型,定义不同的 getter、setter 方法和一些判断边界值的接口。getter 方法是得到表格中的具体控件对象,setter 方法是对具体的控件设值。另外的一些接口则是一些类似于验证当前文本框是否包含某些值的判断操作,或者点击动作等。具体接口定义如下,以控件类型划分,表格的形式呈现。由于以下接口是属于公共方法,所有对 GUI 界面的操作都可以使用,故在 tasks 层对其进行定义和实现。而这些接口所要操作的表格,则应放在 appobjects 层,作为 GUI 元素存在。
目的 | 方法名 | 返回值 | 参数 |
向文本输入框中填写指定值 | setTextEntryFromTable | void | StatelessGuiSubitemTestObject table –文本输入框所在表格 String name –文本输入框的 name 属性值 String value –准备向文本输入框中填写的值 |
得到文本输入框对象 | getTextEntryFromTable | TextGuiTestObject | StatelessGuiSubitemTestObject table –文本输入框所在表格 String name –文本输入框的 name 属性值 |
得到当前文本输入框中已经填写的值 | getCurrentTextInTextEntryFromTable | String | StatelessGuiSubitemTestObject table –文本输入框所在表格 String name –文本输入框的 name 属性值 |
当前文本输入框中的值是否与预期值一致 | isValidValueInTextEntryFromTable | boolean | StatelessGuiSubitemTestObject table –文本输入框所在表格 String name –文本输入框的 name 属性值 String value –文本框中的预期值 |
表 1.2 单选框 (radio button) 接口
目的 | 方法名 | 返回值 | 参数 |
点选单选框 | setRadioButtonFromTable | void | StatelessGuiSubitemTestObject table –单选框所在表格 String id –单选框的 id 属性值 |
得到单选框对象 | getRadioButtonFromTable | ToggleGUITestObject | StatelessGuiSubitemTestObject table –单选框所在表格 String id –单选框的 id 属性值 |
表 1.3 下拉框 (drop down list) 接口
目的 | 方法名 | 返回值 | 参数 |
选择下拉框中指定值的选项 | setComboxFromTable | void | StatelessGuiSubitemTestObject table –下拉框所在表格 String name –下拉框的 name 属性值 String value –拟选择的下拉框值 |
选择下拉框中第一个值 | setComboxFromTableWithoutValue | void | StatelessGuiSubitemTestObject table –下拉框所在表格 String name –下拉框的 name 属性值 |
得到下拉框对象 | getComboxFromTable | SelectGuiSubitemTestObject | StatelessGuiSubitemTestObject table –下拉框所在表格 String name –下拉框的 name 属性值 |
得到当前下拉框中选择的值 | getCurrentSelectionInComboxFromTable | String | StatelessGuiSubitemTestObject table –下拉框所在表格 String name –下拉框的 name 属性值 |
当前下拉框中的值是否与预期值一致 | isValidValueInComboxFromTable | boolean | StatelessGuiSubitemTestObject table –下拉框所在表格 String name –下拉框的 name 属性值 String value –下拉框当前选项的期望值 |
表 1.4 链接 (link) 接口
目的 | 方法名 | 返回值 | 参数 |
点击链接 | clickLinkInTable | void | StatelessGuiSubitemTestObject table –链接所在表格 String textAttribute –链接的 text 属性值 |
表 1.5 按钮 (button) 接口
目的 | 方法名 | 返回值 | 参数 |
点击图片按钮 | clickButtonInTable | void | StatelessGuiSubitemTestObject table –图片按钮所在表格 String altAttribute –图片按钮的 alt 属性 |
点击第 n 个具有相同 alt 属性的按钮 | clickButtonInTable | void | StatelessGuiSubitemTestObject table –图片按钮所在表格 String altAttribute –图片按钮的 alt 属性 int currentIndex –拟选择的第 n 个按钮 |
点击 submit 按钮 | clickSubmitButtonInTable | void | StatelessGuiSubitemTestObject table – submit 按钮所在表格 String attribute –按钮的 value 属性值 int currentIndex –拟选择第 n 个按钮 |
本部分以文本输入框为例,具体给出如何实现上述接口。在实现中用到了 ibm package (ibm.jar),这个 package 最后发布日期是 2007 年 7 月 10 日,IBM 声明可以给客户使用,但不再对该这个 jar 包的代码提供技术支持,包括处理功能增强和 defect 报告的请求。该包中含有在父对象中查询子对象的方法。
package common.tasks; import ibm.widgets.ObjectFactory; // 向一个文本框中写入指定的文本 //table –包含文本输入框的表格 //name –文本输入框的 .name 属性值 //value - 拟填入到文本框中的值 public static void setTextEntryFromTable( StatelessGuiSubitemTestObject table, String name, String value) { String propertyName = ObjectFactory.gsHtmlTFProp; String classID = ObjectFactory.gsHtmlTFClass; try { TextGuiTestObject textEntry =new TextGuiTestObject( ObjectFactory.findTestObject(name, propertyName, classID, table)); if(textEntry != null) { textEntry.waitForExistence(); textEntry.setText(value); textEntry.unregister(); } textEntry = null; } catch(Exception e) { } } // 在表格中找到一个特定的文本输入框 //table –包含文本输入框的表格 //name –文本输入框的 .name 属性值 public static TextGuiTestObject getTextEntryFromTable( StatelessGuiSubitemTestObject table, String name) { String propertyName = ObjectFactory.gsHtmlTFProp; String classID = ObjectFactory.gsHtmlTFClass; try { return new TextGuiTestObject(ObjectFactory.findTestObject(name, propertyName, classID, table)); } catch(Exception e) { } return null; } // 得到当前文本输入框中当前的文本 //table –包含文本输入框的表格 //name –文本输入框的 .name 属性值 public static String getCurrentTextInTextEntryFromTable ( StatelessGuiSubitemTestObject table, String name) { String propertyName = ObjectFactory.gsHtmlTFProp; String classID = ObjectFactory.gsHtmlTFClass; String currentText = ""; try { TextGuiTestObject textEntry =new TextGuiTestObject( ObjectFactory.findTestObject(name, propertyName, classID, table)); if(textEntry != null) { textEntry.waitForExistence(); currentText = textEntry.getText(); textEntry.unregister(); return currentText; } textEntry = null; } catch(Exception e) { } return currentText; } // 判断文本框中的当前值与指定的文本是否一致 //table –包含文本输入框的表格 //name –文本输入框的 .name 属性值 //value - 拟填入到文本框中的值 public static boolean isValidValueInTextEntryFromTable( StatelessGuiSubitemTestObject table, String name, String value) { String propertyName = ObjectFactory.gsHtmlTFProp; String classID = ObjectFactory.gsHtmlTFClass; String currentText = ""; try { TextGuiTestObject textEntry =new TextGuiTestObject( ObjectFactory.findTestObject(name, propertyName, classID, table)); if(textEntry != null) { textEntry.waitForExistence(); currentText = textEntry.getText(); textEntry.unregister(); if(currentText.equalsIgnoreCase(value)) { return true; } else { return false; } } textEntry = null; } catch(Exception e) { } return false; } |
应用上述接口,当界面发生变化时,能做到脚本改动最小,并且快速扩展脚本功能。例如在一个 form. 表单中原本有“姓名”和“邮件”两个输入框,现在加入一个“地址”输入框,位置在姓名和邮件之间。页面的变动会造成已存在控件的属性发生不同程度的改变,在传统的基于控件对象的开发中,将导致对象不可识别。为了保证脚本顺利运行,唯有在 appobjects 层重新抓取所有的控件,包括新引入的控件,然后在 task 层加入对新控件的操作。应用了上述接口之后,我们将发现在 appobjects 层的对象没有任何改动,只是在 task 层新加入一条语句,用来在表单中找到新引入的控件对象并对其进行操作,从而节省了很多繁杂的对象抓取、维护和代码改动工作。如下面的代码所示。
package tasks; import common.appobjects.ObjForm; import common.tasks.TaskTableOperation; public boolean fillForm() { StatelessGuiSubitemTestObject formTable = ObjForm.getFormTable(); TaskTableOperation.setTextEntryFromTable(formTable, “name”, “IBMer”); TaskTableOperation.setTextEntryFromTable(formTable, “locatioin”, “Beijing”); TaskTableOperation. setTextEntryFromTable (formTable, “email”, “IBMer@cn.ibm.com”); return true; } |
作者; 关 承恩, 软件工程师, IBM
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14780914/viewspace-604218/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14780914/viewspace-604218/