实例解析AUTOMATION TEST FRAMEWORK (2)
上一篇 / 下一篇 2013-03-25 10:29:57 / 个人分类:测试
WATIR, WEB DRIVER,CUCUMBER测试框架分析和建立产品框架的过程
而Watir Web Driver就是将二者合二为一形成一个新的测试框架。
Cucumber是一个能够理解用普通语言描述的测试用例的支持行为驱动开发(BDD)的自动化测试工具。
我们可以通过Cucumber + Watir Web Driver 实现一个功能更加强大的BDD测试框架。
Cucumber框架的目的很简单:
由自然语言书写的Feature文件>>通过Define Step进行匹配>>调用相匹配的Watir Web Driver实现的自动化测试脚本。
所以,我们的主要工作就在于如何使用Watir Web Driver进行自动化脚本的实现。
首先要知道Web Driver是一个独立的framework,并没有依赖于任何现有的framework,所以在跟其他framework集成时,不需要做过多的屏蔽工作。
Web Driver的目的,就是作为一个简单有效的工具,来进行web application的自动化测试。它提供了能够直接被其他框架使用的API。
所以,Web Driver的作用就是简化了对Browser操作的封装。它已经包含了足够使用的方法,使得你在创建自己的框架时,可以无需再提供额外的Browser操作的封装模块。
Watir Web Driver,等于是将Watir和Web Driver融合,使用Watir来进行页面元素,操作,数据控制等模块,使用Web Driver来达到在Browser上对这些元素进行控制的目的。
而我们在项目实际应用中,需要根据具体情况再次封装我们的产品,从而实现特定产品的test framework。
对于web driver,我们无需理会,它等于封装了底层的对browser的操作。我们需要了解的是Watir 里,如何对页面元素进行控制。
一般来说,对于一个web页面进行自动化测试的框架,无非需要实现以下几个功能模块:
页面元素的查找
页面元素的操作
页面元素的判断
页面元素的获取
页面元素的断言
错误处理
数据驱动
日志管理
执行管理
我们可以看出,这个framework的核心就是围绕着element进行的。
对于Watir Web Driver Framework来说,element的所有控制都是基于该元素的tag进行分类的,这就保证了它对任何语言和任何浏览器能够进行操控,只要该产品的页面是 遵循HTML标准的。
我们来看看,它能支持的element对象有哪些:
Area - <area>
Button - <input type=”button”>, <input type=“image”>, <input type=”reset”>, <input type=”submit”>
Cell - <td>
Checkbox - <input type=”checkbox”>
Div - <div>
File_field - <input type=”file”>
Form. - <form>
Frame. - <frame>, <iframe>
H1, H2, H3, H4, H5, H6 - <h1>, <h2>, <h3>, <h4>, <h5>, <h6>
Hidden - <input type=”hidden”>
Image - <img>
Label - <label>
Li - <li>
Link - <a>
Map - <map>
P - <p>
Pre - <pre>
Radio - <input type=”radio”>
Row - <tr>
Select_list - <select>
Span - <Span>
Table - <table>
Text_field - <input type=”password”>, <input type =”text”>, <textarea>
Ul - <ul>
从上面的列表,我们看到,基本上HTML元素都被涵盖在内了。这样,我们在封装我们自己的方法时,可以使用完全OO的思想,来封装我们要进行的操作。
作为一种纯粹OO的语言,ruby和纯粹的OO的自动化框架Watir,它提供的element的通用方法有如下:
==是否相等
attribute_value返回指定属性的值
add_checker添加checker
browser返回所在browser对象
click左键单击
clear清除选择
close关闭
double_click左键双击
drag_and_drop_by拖拽到指定区域
drag_and_drop_on拖拽到指定元素上
driver返回所用的driver
enabled?是否可以点击
exists?是否存在
fire_event执行指定的事件
flash高亮该对象
focus焦点移至该对象上
focused?焦点是否在对象上
hash返回该对象的hash值
hover鼠标悬浮在该对象上
html返回该对象的html内容
inspect返回该对象的inspect值
parent返回该对象的父节点
present?是否存在并可见
right_click右键点击一次
run_checkers返回正在运行的对象列表
style返回对象的style值
set设置值
send_keys发送值参
tag_name返回对象的tag值
text返回对象的text值
to_subtype返回对象的子类型
type返回对象的类型
value返回对象的value值
visible?是否可见
wd返回该对象的driver值
wait_until_present对象出现前等待
waith_while_present对象消失前等待
when_present对象出现前等待
这些方法是基本所有element都通用的方法。之所以这些方法没有封装到一个方法类总库,就是因为watir要实现的纯粹OO的框架,所以,所有的操作都是基于实例化的element上,而不是element再继承方法抽象类来实现自己的方法。
这点和Java的理念有所不同。Java 中, 对象是对象,方法是对象的属相,而在基于ruby的Watir中,对象是对象,方法也是对象,所有的一切都是封装好的对象。
当然,每个element都还有一些私有的方法,满足各自的不同需求。我们也可以根据这些element,来组合一个新的element来重载或者继承多种方法,来满足一些特殊情况的需要。
比如,我们完全可以把Login action 封装成一个login对象。该Login对象是通过user name 和password 这两个子元素,以及login button元素组合形成的业务模块。
对于user name和password都分别 属于一个text_field 元素,而 login button属于一个button元素。我们把它们封装成为一个login元素。
当我们查找这个元素时,需要同时匹配user name,password和login button三个属性值,然后调用内置的方法set和click 形成一个新的方法login action。
通过上面的例子,我们对基于业务模块或者商业逻辑如何封装产品有了个初步的了解。
下面我们要了解最终的核心部分,那就是如何获取页面的元素。
获取页面元素,其实就是如何能够定位该页面元素,而定位的方法,就是根据该元素的一些属性信息进行查找。这些属性信息有哪些呢,让我们来一起看一下。
因为watir是使用标准的html tag来作为对象来进行识别的,所以这些元素的属性信息,基本上和html中每个tag元素具有的属性相同,这种方式就大大减少了我们学习新封装方法的工作量。
我们来看看,每个tag都有哪些属性可以用来进行定位:
Area - <area> - :class, :id, :index, :name, :text
Button – <button> - :alt, :class, :id, :index, :name, :text, :value
- <input type=”button”> - :alt, :class, :id, :index, :name, :text, :value
- <input type=“image”> - :alt, :class, :id, :index, :name, :src, :text, :value
- <input type=”reset”> - :alt, :class, :id, :index, :name, :text, :value
- <input type=”submit”> - :alt, :class, :id, :index, :name, :text, :value
Cell - <td> - :class, :id, :index, :name, :text
Checkbox - <input type=”checkbox”> - :class, :id, :index, :name, :text, :value
Div - <div> - :class, :id, :index, :name, :text
File_field - <input type=”file”> - :class, :id, :index, :name, :title, :value
Form. - <form> - :action, :class, :id, :index, :method, :name
Frame. - <frame>, <iframe> - :id, :index, :name, :src, :text
H1, H2, H3, H4, H5, H6 - <h1> - :class, :id, :index, :name, :text
-<h2> - :class, :id, :index, :name, :text
-<h3> - :class, :id, :index, :name, :text
-<h4> - :class, :id, :index, :name, :text
-<h5> - :class, :id, :index, :name, :text
-<h6> - :class, :id, :index, :name, :text
Hidden - <input type=”hidden”> -:class, :id, :index, :method, :name, :text, :value
Image - <img> - :alt, :class, :for, :id, :index, :name, :src, :text, :value
Label - <label> - :class, :id, :index, :name, :text
Li - <li> - :class, :id, :index, :name, :text
Link - <a> - :after?, :class, :href, :id, :index, :name, :text
Map - <map> - :class, :id, :index, :name, :text, :value
P - <p> - :class, :id, :index, :name, :text
Pre - <pre> - :class, :id, :index, :name, :text
Radio - <input type=”radio”> - :class, :id, :index, :name, :text, :value
Row - <tr> - :class, :id, :index, :name, :text
Select_list - <select> -:class, :id, :index, :name, :text, :value
Span - <Span> - :class, :id, :index, :name, :text
Table - <table> - :class, :id, :index, :name, :text
Text_field - <input type=”password”> - :class, :id, :index, :name, :text, :value
- <input type =”text”> - :class, :id, :index, :name, :text, :value
- <textarea> - :class, :id, :index, :name, :text, :value
Ul - <ul> - :class, :id, :index, :name, :text
我们用一些具体的例子来更好的理解这些。
对于一个具有class,id,index,name,和text属性的area,我们可以按如下方法查找:
•Browser.area(:class, “area class name”)
•Browser.area(:id, “area id”)
•Browser.area(:index, 0)
•Browser.area(:name, “area name”)
•Browser.area(:text, “area text”)
我们也可以任意选取其中几个属性来更精确的定位:
•Browser.area(:class=>“area class name”, :id=>“area id”, :index, 0)
另外,我们为了更好的,或者说更无奈的方法,是采用Xpath定位。Xpath定位很强大,但是经常会出现多个match对象时返回并非需要的对象,并且造成代码过于复杂的识别,影响执行速度,所以Xpath定位要少用。
•Browser.area(:xpath, “//area1”)
Waitr直接采用了ruby本身的File来处理Data-driven的行为。
我们来看一个操作Excel文档的实例:
require 'win32ole'
#支持excel文件操作
require 'watir-webdriver'
#watir web driver支持
#打开数据文件
excel = WIN32OLE::new('excel.Application')
workbook = excel.Workbooks.Open('d:\rubycode\test.xlsx')
#激活指定的tab
worksheet = workbook.Worksheets('test')
worksheet.Activate
#读入用户名和密码信息
@username = worksheet.Range('a1').Value
@password = worksheet.Range('b1').Value
#关闭文件
excel.Quit
#使用Watir webdriver进行自动化测试
#设置Base URL
$BASE_URL=http://10.32.148.243:8080
#生成webdriver的实例
$browser=Watir::Browser.new:ie
#转向指定的URL
$browser.goto($BASE_URL+'/parkinglot/login')
#判断登录界面出现
Watir::Wait.until {$browser.text.include?('停车场管理系统登录')}
#写入从数据文件中得出的测试数据
$browser.text_field(:name, "username").set(@username)
$browser.text_field(:name, "password").set('1234')
#执行登录
$browser.div(:class, "login_btn").click
#确认正确登录到主页
Watir::Wait.until {$browser.text.include?('Copyright')}
从上例可知,watir webdriver 只是提供了如何获得element的方法。文件操作的方法,都是直接调用了已有的Ruby on Rails框架中的方法。 而对浏览器的操作,也是调用的Web Driver提供的方法。
这在我们做测试框架中很常见,我们可以再次体会到,框架就是用来被引用,或者被其他框架所包含,从而提供更丰富的API接口这种理念。
现在我们已经理清了这套框架的基本架构:
Ruby on Rails框架,提供给了Watir Web Driver框架的底层应用,例如文件的操作
Web Driver框架,提供给了Watir Web Driver框架对Browse进行操作的方法
Watir Web Driver框架本身提供了对页面元素进行识别,操作的方法
Cucumber框架则提供了一个可以用自然语言去调用Watir Web Driver框架的方法
好了,明白了这一切,我们就可以动手做自己的Automation Test Framework了。当然,一般来说,选用这些现成的框架即可。
我们真正需要做的,就是如何封装自己的产品。封装的方式无外乎这几种:
基于Page的封装
基于流程的封装
基于业务逻辑的封装
基于功能模块的封装
基于行为的封装
基于数据模型的封装
一般来说,怎么封装都可以,每一种封装方式都有自己的优缺点,所以在封装不同的东西选择不同的封装方式很常见。比如上面提到的例子,login的封装,你可以理解为基于Page的封装,也可以看作是行为的封装,甚至是流程的封装等等,你的着眼点不同,它的意义也就不同。
一般来说,封装的目的就是形成三层结构。现有的框架我们可以认为是底部驱动层。而我们依据产品的封装可以看成是逻辑层,而我们测试用例就是所谓的应用层。
而Cucumber + Watir Web Driver也可以进行任意的封装,但是最好的封装方式,是基于对象的方式。为什么这么说呢,我们通过实际的例子来看一下。
Cucumber执行自动化测试用例,是通过feature文件来实现的,我们拿一个feature文件来看看。
# language: zh-CN
功能:区域管理
作为一个管理人员
我能够进行触摸屏管理
场景: Case 1 - 添加触摸屏-确定(S2-ST-JBXXGL-CMPGL-0001)
假如进入触摸屏管理界面
而且按下新增按键
而且在触摸屏名称里输入ATT
而且从所属楼层列表中选择地下一层
而且在触摸屏IP地址里输入188.0.0.1
而且在触摸屏X坐标里输入88
而且在触摸屏Y坐标里输入99
而且按下触摸屏确定按键
当判断预期结果时
那么可以看到ATT
场景: Case 2 - 添加触摸屏-取消(S2-ST-JBXXGL-CMPGL-0002)
假如进入触摸屏管理界面
而且按下新增按键
而且在触摸屏名称里输入Touch
而且从所属楼层列表中选择地下一层
而且在触摸屏IP地址里输入188.0.0.1
而且在触摸屏X坐标里输入88
而且在触摸屏Y坐标里输入99
而且按下触摸屏取消按键
当判断预期结果时
那么看不到Touch
场景: Case 3 - 修改触摸屏-取消(S2-ST-JBXXGL-CMPGL-0003)
假如进入触摸屏管理界面
而且触摸屏选择值为ATT
而且按下修改按键
而且在触摸屏名称里输入A
而且按下触摸屏取消按键
当判断预期结果时
那么看不到ATTA
场景: Case 4 - 修改触摸屏-确定(S2-ST-JBXXGL-CMPGL-0004)
假如进入触摸屏管理界面
而且触摸屏选择值为ATT
而且按下修改按键
而且在触摸屏名称里输入A
而且按下触摸屏修改按键
当判断预期结果时
那么可以看到ATTA
场景: Case 5 - 删除触摸屏-取消(S2-ST-JBXXGL-CMPGL-0005)
假如进入触摸屏管理界面
而且触摸屏选择值为ATTA
而且按下删除按键
而且按下确定取消按键
当判断预期结果时
那么可以看到ATTA
场景: Case 6 - 删除触摸屏-确定(S2-ST-JBXXGL-CMPGL-0006)
假如进入触摸屏管理界面
而且触摸屏选择值为ATTA
而且按下删除按键
而且按下确定删除按键
当判断预期结果时
那么看不到ATTA
我们可以看到,对于user story:
功能:区域管理
作为一个管理人员
我能够进行触摸屏管理
我们一共有6个场景来保证该story已经能够被完成,至于有bug在内,那不是判断story是否可以被认定为完成的依据,那是交付的依据。
我们随便取其中的一个场景来分析:
场景: Case 5 - 删除触摸屏-取消(S2-ST-JBXXGL-CMPGL-0005)
假如进入触摸屏管理界面
而且触摸屏选择值为ATTA
而且按下删除按键
而且按下确定取消按键
当判断预期结果时
那么可以看到ATTA
第一行,我们可以认为是一个navigation的过程,那么所有的navigation操作,可以作为一个通用的流程进行封装。那么封装的方式就可以是:
Given /进入(.*)界面/ do |module_name|
$autotest.navigate_to_module(module_name)
end
从上面可以看出,它是根据要转向的module name,或者是页面名称来进行转向的,是一个纯粹的业务流程的封装。
但是,我们也可以基于Page去封装,那么又如何去书写语句呢?
我们可以把每个Page都当作一个对象,那么我们就把“触摸屏管理界面”封装成一个名叫touchscreen的page对象,那么上面的语句就可以写成:
Given /进入*界面/ do |module_name|
if module_name ==” 触摸屏管理” then
$autotest.touchscreen.visit();
end
end
我们可以看到,这么封装的方式好处是page上所有的操作都可以封装到一个page对象中,当该页面发生跟新时,我们只需要更新该page对象即可,这在以前是很流行的封装方式。
但是如果考虑到这种情况呢:
页面里面有个element,比如search button,是被很多页面共同使用的。在基于page的封装方法中,你不得不在每一个包含该search button的页面中进行封装。一旦该元素进行了修改,那么意味着你需要更新所有的page对象。 在这个过程中,很容易犯错。
所以cucumber推崇的是基于最基本的页面元素的封装。例如button的封装:
#按下xx按键
Given /按下(.*)按键/ do |button_name|
$autotest.click_button(button_name)
end
它是封装了button的click事件。这样所有的button的click都可以调用这一个统一的方法,我们也可以完全基于某个element做属性封装,例如:
#按下xx按键
Given /(..)(.*)按键/ do |button_action, button_name|
$autotest.button(button_name, button_action)
end
而在button中封装了一个方法就是Button.action(button_action), 这里key就是button_action=click。从而触发这个click事件。
所以说,那种封装方式不重要,重要的是怎么能减少你后期维护的时间和精力,以及如何更大限度的复用代码,这两者必须达到平衡,不能偏向任何一边。
TAG:
标题搜索
日历
|
|||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
1 | 2 | 3 | 4 | 5 | 6 | ||||
7 | 8 | 9 | 10 | 11 | 12 | 13 | |||
14 | 15 | 16 | 17 | 18 | 19 | 20 | |||
21 | 22 | 23 | 24 | 25 | 26 | 27 | |||
28 | 29 | 30 |
我的存档
数据统计
- 访问量: 150204
- 日志数: 185
- 文件数: 6
- 建立时间: 2007-08-06
- 更新时间: 2015-01-06