-
Ruby语言入门(24)- 内部类 - Thread
2013-03-18 15:17:47
Thread使得并行编程成为可能。线程是一种共享内存空间并可同时运行的控制流。目前Ruby解释器采用时间片轮转法来控制线程,所以使用线程后并不会提高程序的运行速度。启动程序时生成的线程被称作主线程。若主线程因为某些原因而终止运行时,子线程和整个程序也会终止运行。发生异常时,会被送往主线程。若启动线程时指定的块结束运行时,该线程也将终结。块的正常结束和因异常等引起的非正常结束都代表快的终结。当线程内发生异常,而且没有被rescue捕到时,该线程会被停掉,而且不发出任何警告。如果这时有其它线程因Thread#join而等待这个线程,会在等待的线程中再次引发相同的异常。通过下列方法可以保证在线程因发生异常而终止工作时,中断解释器:$DEBUG的值设为真(调试模式)。用-d选项来启动ruby解释器也可以取得相同的效果。使用Thread.abort_on_exception来设置标识。使用Thread#abort_on_exception来设定特定线程的标识。线程的状态可以使用Object#inspect或Thread#status来查看线程的状态。 状态有:run (运行 or 可运行状态)sleep (挂起状态)aborting (终止处理中)dead (终止状态)Thread.abort_on_exceptionThread.abort_on_exception = newstate若其值为真的话,一旦某线程因异常而终止时,整个解释器就会被中断Thread.criticalThread.critical = newstate当其值为真时,将不会进行线程切换Thread.current返回当前运行中的线程Thread.exit终止当前线程的运行。Thread.kill(thread)终止指定线程的运行。若该线程已经终止,则不作任何动作。Thread.list返回处于运行状态或挂起状态的活线程的数组。Thread.main返回主线程Thread.new([arg, ...]) { ... }Thread.start([arg, ...]) { ... }Thread.fork([arg, ...]) { ... }生成线程,并开始对块进行计算.Thread.pass将运行权交给其他线程. 它不会改变运行中的线程的状态,而是将控制权交给其他可运行的线程Thread.stop将当前线程挂起,直到其他线程使用run方法再次唤醒该线程.self[name]取出线程内与name相对应的固有数据.self[name] = val将线程内与name相对应的固有数据的值设为valabort_on_exceptionabort_on_exception = newstate它返回布尔值,在赋值形式中,它返回右边的newstate。alive?若线程是"活"的,就返回true.exitkillterminate终止线程的运行.group返回线程所属的ThreadGroup对象.joinjoin(limit)挂起当前线程,直到self线程终止运行为止. 若self因异常而终止时, 将会当前线程引发同样的异常.key?(name)若与name相对应的线程固有数据已经被定义的话,就返回truekeys以数组的形式返回与线程固有数据取得关联的索引.priority返回线程的优先度. 优先度的默认值为0. 该值越大则优先度越高.priority = val设定线程的优先度raise([error_type,][message][,traceback])在该线程内强行引发异常.run重新启动被挂起(stop)的线程.safe_level返回self 的安全等级.status使用字符串"run"、"sleep"或"aborting" 来表示活线程的状态.stop?若线程处于终止状态(dead)或被挂起(stop)时,返回true.value一直等到self线程终止运行(等同于join)后,返回该线程的块的返回值.wakeup把被挂起(stop)的线程的状态改为可执行状态(run). -
Ruby语言入门(23)- 内部类 -Struct
2013-03-18 14:44:23
Struct为结构体类。由Struct.new生成该类的子类。在子类中使用new方法就可以生成构造体。构造体子类中定义了构造体成员的访问方法。Struct.new([name,] member ... )生成并返回一个名为name的Struct 类的子类。子类中定义了访问结构体成员的方法.结构体名name将成为Struct的类常数名,所以必须以大写字母开始。Struct::XXX.new(value,...)Struct::XXX[value,...]生成并返回结构体对象。参数将成为结构体的初始值。Child = Struct.new("Child", :name, :age) //由Struct.new生成该类的子类。child1 = Child.new("xixi", 5) //在子类中使用new方法就可以生成构造体。child1.age //构造体子类中定义了构造体成员的访问方法。#=> 5child2.name#=>xixiself[nth]返回结构体的第nth个成员的值。child1[1]#=> 5 //child1[1] --> child1.ageself[nth]=value将结构体的第nth个成员的值设定为value,并返回value值。child1[1]=11child1[1]#=> 11each {|value| ... }依次对结构体的各个成员进行迭代操作。child1.each {|v| puts v}#=>xixi11each_pair {|member, value| ... }在结构体中,依次将某成员的名称和值传给块进行迭代操作。child1.each_pair{|m,v| p m,v}#=>:namexixi:age11length/size返回结构体的成员数量。child1.legth#=>2members以数组形式返回结构体的成员名(字符串)。child1.members#=>[:name, :age]values/to_a将结构体的成员的值存入数组,并返回它。child1.values#=>["xixi", 11]values_at(member_1, ... member_n)以数组的形式返回参数(数值、字符串或Symbol)所指成员的值。child1.values_at(1)#=> 11 -
使用Ruby对XML进行操作
2013-03-06 15:55:49
Ruby是用REXML库对XML文件进行解析,路径是: rexml/document所有方法全部包含在模块 module REXML 中,所以在文件头部引用的时候使用如下格式:require 'rexml/document'
include REXML首先我们要打开指定的XML文件:xmlDoc=File.new('c:\\nodes.xml')xmlFile=Document.new(xmlDoc)newDocument类的构造方法,参数可以为一个xml文件的路径,或者一个IO对象。puts xmlFile.root输出为:<root>Root Node<Child1 att1='attvalue1' att2='attvalue2'>Son<Child2>grandson</Child2></Child1></root>root返回一个element类型的对象,是该xml的根元素。 注意,这时候不是document对象了,是一个element对象。puts xmlFile.root.document输出为:<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><root>Root Node<Child1 att1='attvalue1' att2='attvalue2'>Son<Child2>grandson</Child2></Child1></root>document返回包含element类型对象的文档, 注意,这时候是document对象了。puts xmlFile.root.get_text.valueputs xmlFile.root.get_textputs xmlFile.root.textputs xmlFile.root.texts输出均为:Root Nodetext: 返回第一个text element的string值,为String类texts:返回的所有text element的集合,为Array类get_text:返回的是一个REXML::Text对象value:返回REXML::Text对象的值puts XPath.first(xmlFile, "//Child1" )输出为:<Child1 att1='attvalue1' att2='attvalue2'>Son<Child2>grandson</Child2></Child1>使用XPath找到第一个满足Child1的节点,类型为elementXPath.each(xmlFile, "//") { |element| puts element }返回所有的elementputs xmlFile.get_elements("//Child2")输出为:<Child2>grandson</Child2>使用xpath查找匹配的elementxmlFile.root.xpath输出为:/root显示element的xpath,可以看到具体的xpath路径puts xmlFile.root.elements[1]输出为:<Child1 att1='attvalue1' att2='attvalue2'>Son<Child2>grandson</Child2></Child1>puts xmlFile.root.elements[1].elements[1]输出为:<Child2>grandson</Child2>以上都是利用index定位element,注意index都是从1开始的,没有0 -
Ruby利用封装模块来创建XML文档
2013-03-06 10:49:54
我们来看看怎么通过定制可复用的函数来创建xml文档:#coding: utf-8require 'win32ole'#创建xml文件$xmlDoc = WIN32OLE.new('Msxml2.DOMDocument.3.0')#创建Root节点$Root=$xmlDoc.createElement("RootNode")$xmlDoc.appendChild $Root#创建新的节点,并作为指定节点child加入def newXmlNode(newNodeName, newNodeValue, parentNode)$newnode = $xmlDoc.createElement(newNodeName)currentNode=parentNode.appendChild($newnode)currentNode.text = newNodeValuereturn $newnodeend#创建指定节点的属性def addNodeAttribute(xmlNodeName,newAttribute, newAttributeValue)attrs = $xmlDoc.createAttribute(newAttribute)attrs.value=newAttributeValuexmlNodeName.setAttributeNode(attrs)end#创建一个子节点,作为Root的子节点加入newnode1=newXmlNode('Child1','Son',$Root)#给指定的子节点添加属性1addNodeAttribute(newnode1,'att1','attvalue1')#创建一个子节点,并作为newnode1的子节点加入newnode2=newXmlNode('Child2','grandson',newnode1)#给指定的子节点继续添加属性2addNodeAttribute(newnode1,'att2','attvalue2')#保存xml文件$xmlDoc.save('C:/nodes.xml')运行结果如下: -
使用Ruby生成XML文件
2013-03-05 18:40:16
下文为一个ruby生成多层xml文档的实例:require 'win32ole'#创建xml文件xmlDoc=WIN32OLE.new("MSXML2.DOMDocument")#设置RootRoot=xmlDoc.createElement("Root")xmlDoc.appendChild Root#创建各层子元素#层1bc1=xmlDoc.createElement("child_1")bc1Attribute=xmlDoc.createAttribute("Level-1")bc1Attribute.text="1"bc1.text="1"bc1.setAttributeNode bc1Attribute#层2bc2=xmlDoc.createElement("child_2")bc2Attribute=xmlDoc.createAttribute("Leve-2")bc2.setAttributeNode bc2Attributebc2Attribute.text="2"bc2.text="2"#层3bc3=xmlDoc.createElement("child_3")bc3.text="3"#层4-1bc41=xmlDoc.createElement("child_4_1")bc41.text="4-1"#层4-2bc42=xmlDoc.createElement("child_4_2")bc42.text="4-2"#加入各自父节点下bc3.appendChild bc41bc3.appendChild bc42bc2.appendChild bc3bc1.appendChild bc2Root.appendChild bc1#创建 XML processing instruction 并把它加到根元素之前header=xmlDoc.createProcessingInstruction("xml","version='1.0'")xmlDoc.insertBefore header,xmlDoc.childNodes(0)#文件保存xmlDoc.Save "c:\\test.xml"执行结果如下: -
使用excel进行Data Driven的实例
2013-03-05 17:28:02
基于ruby的watir web driver# encoding: utf-8#要求支持中文require 'win32ole'#支持excel文件操作require 'watir-webdriver'#watri web driver支持#打开数据文件,读入用户名和密码信息excel = WIN32OLE::new('excel.Application')workbook = excel.Workbooks.Open('d:\rubycode\test.xlsx')worksheet = workbook.Worksheets('test')worksheet.Activate@username = worksheet.Range('a1').Value@password = worksheet.Range('b1').Valueexcel.Quit#使用Watir 进行自动化测试$BASE_URL="http://10.32.148.243:8080"$browser=Watir::Browser.new:ie$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").clickWatir::Wait.until {$browser.text.include?('Copyright')}很简单的一个示例 -
Watir Web Driver 对excel 文件的操作
2013-03-05 15:56:23
一般我们在Watir中实现data driven时,是依靠ruby的file函数。下面是如何使用excel文件进行data-driven的。打开文件需要首先require 'win32ole'然后打开文件excel = WIN32OLE::new('excel.Application')workingbook = excel.Workbooks.Open('d:\rubycode\test.xlsx')然后选择需要的sheet#依据sheet的名称选择workingsheet = workingbook.Worksheet('test')#或者依据顺序号选择workingsheet = workingbook.Worksheets(1)#激活可以用select
workingsheet.Select#也可以用Activateworkingsheet.Actviate然后选择你需要的数据#第一个cell的值workingsheet.Range('a1').Value#第一列的1-10的值worksheet.Range('a1:a3').Value#第一行的a-g的值worksheet.Range('a1:b1:c1:d1:e1:f1:g1').Value或者worksheet.Range('a1:g1').Value#任意cell的值worksheet.Range('a1:b2:c3:d4:e5:f2:g9').Value#第一列第一个到第三列第三个,即a1,a2,a3,b1,b2,b3,c1,c2,c3worksheet.Range('a1:c3').Value#判断第一列的非空行数(注意,返回的是第一个空cell)line=1cells='a1'while worksheet.Range(cells).Valueline=line+1cells='a'+line.to_send#列出第一列的非空cellline=line-1cells='a'+line.to_sif cells == 'a1'rows='a1'elserows='a1:'+cellsendcelldata = worksheet.Range(rows).Valueputs cell data写入数据#单个数据worksheet.Range('e2').Value= 'test'#数组值worksheet.Range('a5:c5').Value = ['more', 'than', 'one']然后存储workbook.Close(1)#对应的是第一个按键workbook.SaveAs 'mytest.xls'# 默认路径是"My Documents"关闭文件excel.Quit注意,操作时不要打开目标文件。更多的关于excel的操作,可以参考:http://msdn.microsoft.com/zh-cn/library/ff841127(v=office.14).aspx -
Watir Webdriver的editor处理
2013-01-28 15:00:47
很多网站使用了WYSIWYG Editor来输入文本,比如51testing这样的。Watir内置了一些方法可以对其进行处理:一种是先查找到editor所在的iframe,然后使用send_keys的方法来发送字符串,需要注意的是,包含该iframe的窗口必须是在所有的窗口最上方.另外一种方式是书写一段javascript语句,通过对browser进行内容输入,这种方法最常见例如CKEditorrequire 'watir-webdriver'b = Watir::Browser.new :firefoxb.goto 'http://ckeditor.com/demo'b.execute_script("CKEDITOR.instances['editor1'].setData('hello world');")b.frame(:title => 'Rich text editor, editor1, press ALT 0 for help.').send_keys 'hello world again'这个例子就是向iframe直接通过send_keys发送字符而使用TinyMCE Editor的例子,就是执行一段javascript语句require 'watir-webdriver'b = Watir::Browser.newb.goto 'http://tinymce.moxiecode.com/tryit/full.php'b.execute_script("tinyMCE.get('content').execCommand('mceSetContent',false, 'hello world' );")b.frame(:id => "content_ifr").send_keys 'hello world again' -
Watir Webdriver支持的特殊键
2013-01-28 14:02:03
Watir Webdriver可以向element发送特殊的key值,可以有如下几种方式:向browser发送Enter键,等于在当前页面敲击回车,作用于当前焦点处:browser.send_keys
:enter
向指定的element发送多个键值:browser.element.send_keys [
:control
,
'a'
],
:backspace
也可以overide一些元素的click事件:browser.element.click(
:shift
,
:control
)
可以指定的key值有:
:null 空:cancel cancel键:help help键:backspace 退格键:tab 制表符键:clear 清除键:return 回车符键:enter 回车键:shift 右shift键:left_shift 左shift键:control 右ctrl键:left_control 左ctrl键:alt 右Alt键:left_alt 左Alt键:pause 暂停键:escape Esc键:space 空格键:page_up PgUp键:page_down PgDn键:end End键:home Home键:left 左移:arrow_left 左移键:up 上移:arrow_up 上移键:right 右移:arrow_right 右移键:down 下移:arrow_down 下移键:insert 插入键:delete 删除键:semicolon 分号键:equals 等号键:numpad0 数字0键:numpad1 数字1键:numpad2 数字2键:numpad3 数字3键:numpad4 数字4键:numpad5 数字5键:numpad6 数字6键:numpad7 数字7键:numpad8 数字8键:numpad9 数字9键:multiply *号键:add +号键:separator |键:subtract -键:decimal .键:divide /键:f1 F1键:f2 F2键:f3 F3键:f4 F4键:f5 F5键:f6 F6键:f7 F7键:f8 F8键:f9 F9键:f10 F10键:f11 F11键:f12 F12键:meta windows键(windows平台):command 等同:meta键,别名 -
Watir Webdriver的Page Object应用(2)
2013-01-28 13:41:26
如何去创建一个Page Object,下面有一些建议:尽量给每一个page页面创建一个Page Object如果一个页面包含过多的业务逻辑,你可以考虑将它创建为多个Page Object把element的详细处理都封装起来对于测试代码里,永远不要直接去操作element对象,或者操作browser。如果使用Rspec或者Cucumber,在define_step里,不要有直接操作的代码Page Object的目标是当页面发生变化是,你不需要去修改测试代码,而是修改后面的object层不要在Page Ojbect中包含Assertion,断言应该仍在在代码层实现我们来讨论一个实例:browser = Watir::Browser.newbrowser.goto "http://example.com/login"browser.text_field(:name => "user").set "Mom"browser.text_field(:name => "pass").set "s3cr3t"browser.button(:id => "login").clickWatir::Wait.until { browser.title == "Your Profile" }browser.div(:id => "logged-in").should exist这是一个简单的例子,完全按照workflow去操作一个个element对象如果将其Page Object化,那么看其起来是这个样子的:site = Site.new(Watir::Browser.new)login_page = site.login_page.openuser_page = login_page.login_as "Mom", "s3cr3t"user_page.should be_logged_in请注意一下对应关系为了实现Page Obejcts化,我们需要将详细的步骤,封装起来:class BrowserContainerdef initialize(browser)@browser = browserendendclass Site < BrowserContainerdef login_page@login_page = LoginPage.new(@browser)enddef user_page@user_page = UserPage.new(@browser)enddef close@browser.closeendend # Siteclass LoginPage < BrowserContainerURL = "http://example.com/login"def open@browser.goto URLselfenddef login_as(user, pass)user_field.set userpassword_field.set passlogin_button.clicknext_page = UserPage.new(@browser)Watir::Wait.until { next_page.loaded? }next_pageendprivatedef user_field@browser.text_field(:name => "user")enddef password_field@browser.text_field(:name => "pass")enddef login_button@browser.button(:id => "login")endend # LoginPageclass UserPage < BrowserContainerdef logged_in?logged_in_element.exists?enddef loaded?@browser.title == "Your Profile"endprivatedef logged_in_element@browser.div(:id => "logged-in")endend从上面代码可以看出,Page Objects化之后,我们的测试代码都是按照封装好的去书写,例如,将手工测试用例map到自动化测试步骤(Cucumber),在代码更迭之后,我们只需要去修改后面封装的对应代码,从而避免如下的问题发生:1. 漏掉一部分代码没有更新2. 更新错误3. 打乱了原有的代码结构4. 重复代码过多5. 定位错误困难当完成Page Object化之后,跟其他的工具集成就很简单了,例如,在cucumber中我们可以在初始化测试环境中加入一段代码:require "watir-webdriver"require "/path/to/site"module SiteHelperdef site@site ||= (Site.new(Watir::Browser.new(:firefox)))endendWorld(SiteHelper)这样,得到一个更加合理优化的代码结构。而且匹配过程变更加清晰明了:Given /I have successfully logged in/ dologin_page = site.login_page.openuser_page = login_page.login_as "Mom", "s3cr3t"user_page.should be_logged_inend -
Watir Webdriver的Page Object应用(1)
2013-01-28 10:43:18
Page Objects是一种设计模式,用来将页面上的对象进行模块化。通过消除重复的对象,建立起一个抽象基类,从而使得你在开发浏览器自动化测试脚本时有更好的维护性和更强的健壮性。Page Objects可以从两个方面来看待:从测试开发人员的角度来看, 一个Page Object可以看作一个服务。从开发人员的角度来看, 一个Page Object可以看作良好结构的页面对象。对于Page Objects来说,一定要理解,它是提供一个服务的对象,而不用更详细的研究它的方法以及结构的细节。我们来举一个简单的例子,比如一个Web Mail的收件页面, 我们可以把它想象成一个服务,能够提供写信,读信,展示来信的主题等功能,对于我们测试来说,它这些功能是怎么实现的不是我们考虑的范围。对于Page Objects来说,返回值应该是其他的Page Objects,这意味着我们是在不同的Page Objects中进行数据交互。我们用login page来做个例子public class LoginPage {public HomePage loginAs(String username, String password) {// ... 成功的login}public LoginPage loginAsExpectingError(String username, String password) {// ...失败的login}public String getErrorMessage() {// 判断一下错误的内容}}从上面我们可以看到,不但要考虑成功的login到下一个page,也得考虑失败的login是转入那个page,还是在page上显示错误信息。考虑的都是page这个整体需要处理的行为。另外,我们应该考虑的是,我们的测试应该是着重判断page的状态,拿inbox举例子:public void testMessagesAreReadOrUnread() {Inbox inbox = new Inbox(driver);inbox.assertMessageWithSubjectIsUnread("I like cheese");inbox.assertMessageWithSubjectIsNotUnread("I'm not fond of tofu");}我们的断言,是放在page的状态上,也可以这么写:public void testMessagesAreReadOrUnread() {Inbox inbox = new Inbox(driver);assertTrue(inbox.isMessageWithSubjectIsUnread("I like cheese"));assertFalse(inbox.isMessageWithSubjectIsUnread("I'm not fond of tofu"));}当然,我们也要先通过webdriver来判断我们所在的页面是对的,或者说我们要操作的Page Object是已经出现了public class LoginPage {private final WebDriver driver;public LoginPage(WebDriver driver) {this.driver = driver;// 判断我们在正确的页面上if (!"Login".equals(driver.getTitle())) {//如果不在,那就报错throw new IllegalStateException("This is not the login page");}}// 理论上讲,login Page Object能提供Login As的服务public HomePage loginAs(String username, String password) {// 在这里是唯一的详细操作步骤driver.findElement(By.id("username")).sendKeys(username);driver.findElement(By.id("passwd")).sendKeys(password);driver.findElement(By.id("login")).submit();//返回对象应该是一个新的Page Objectreturn new HomePage(driver);}} -
使用Watir Webdriver对页面性能进行测试
2013-01-28 10:34:23
Watir Webdriver自带一个gem Watir-WebDriver-Performance, 可以对页面的性能进行一些简单的测试。方法如下:require 'watir-webdriver'require 'watir-webdriver-performance'b = Watir::Browser.new :chrome10.times dob.goto 'http://watir.com'load_secs = b.performance.summary[:response_time]/1000puts "Load Time: #{load_secs} seconds."end得到的结果如下:Load Time: 3.701 seconds.Load Time: 0.694 seconds.Load Time: 1.874 seconds.Load Time: 1.721 seconds.Load Time: 2.096 seconds.Load Time: 0.823 seconds.Load Time: 2.362 seconds.Load Time: 1.008 seconds.Load Time: 1.761 seconds.Load Time: 2.066 seconds.可以用的方法除了response_time之外,还有::summary:navigation:memory:timing注意,目前只能支持chrome和IE9的版本 -
Watir Webdriver 对JS Dialog的处理
2013-01-28 10:20:52
Watir webdriver内置了如何处理javascript. dialog的方法,以及从dialog获得所需值的方法。# 判断alert是否存在browser.alert.exists?# 获得alert的值browser.alert.text# 关闭alertbrowser.alert.okbrowser.alert.close# 接受confirmbrowser.alert.ok# 取消confirmbrowser.alert.close# 输入内容到promptbrowser.alert.set "Prompt answer"# 接受promptbrowser.alert.ok# 取消promptbrowser.alert.close如果上述方法无效,我们还有一些替代的方法:# 对于alert,overide从而使其不返回任何值browser.execute_script("window.alert = function() {}")# 返回用户在prompt输入的值browser.execute_script("window.prompt = function() {return 'my name'}")# 返回空值,用于模拟点击prompt的cancelbrowser.execute_script("window.prompt = function() {return null}")# 返回true,用于模拟模拟点击confirm的okbrowser.execute_script("window.confirm = function() {return true}")# 返回false,用于模拟点击confirm的cancelbrowser.execute_script("window.confirm = function() {return false}")# 对于离开popup不返回任何值browser.execute_script("window.onbeforeunload = null") -
Watir Webdriver生成文件型log的实例
2013-01-27 17:26:38
我们想获得很好的log日志,这需要我们动手去写,这里有个生成文件log的实例:首先我们有个主log生成器, logfactory.rbrequire 'logger'# default loggerclass LoggerFactory# start loggerdef LoggerFactory.start_default_logger(fileNamePrefix)# time = Time.now.strftime("%m %d %Y %H %M %s")time = Time.now.strftime("%Y%m%d%H%M%S")# logger = CoreLogger.new(File.join(File.dirname(__FILE__), "#{fileNamePrefix}_#{time}.txt") ,logs to keep, maxlogsize)logger = CoreLogger.new(File.join(File.expand_path(".") + "\\logs\\", "#{fileNamePrefix}_#{time}.txt") ,2, 1000000)return loggerendendclass CoreLogger < Logger# extend the logger, overide itdef initialize(fileName, logsToKeep, maxLogSize)super(fileName , logsToKeep, maxLogSize)self.level = Logger::INFO # set to INFO levelself.datetime_format = "%d-%b-%Y %H:%M:%S"self.info("Logger starting...")end#overloaded "log" from logger.rb to something more intuitive.def log(message)puts "log #{message}\n" #optional. comment out if you don't want to see logging in the consoleinfo(message) #calls info in logger.rb -- would be good to use different logging levelsendend然后在我们的case中引入:require 'logfactory'就可以进行使用了:def login_action# Navigate to target URL$browser.goto(TEST_SITE)$logger.log("Passed: Step " + $stepcounter.to_s() + ": Navigate to URL:" + TEST_SITE)$stepcounter=$stepcounter + 1# wait till the user name element show upWatir::Wait.until {$browser.text.include? "USER NAME:"}# enter user name$browser.text_field(:name, "UserName").set(LOGINID)$logger.log("Passed: Step " + $stepcounter.to_s() + ": Enter '" + LOGINID + "' in the User Name text field")$stepcounter=$stepcounter + 1# enter user password$browser.text_field(:name, "UserPassword").set(PASSWORD)$logger.log("Passed: Step " + $stepcounter.to_s() + ": Enter '" + PASSWORD + "' in the Password text field")$stepcounter=$stepcounter + 1# click Login button$browser.button(:value, "Login").click$logger.log("Passed: Step " + $stepcounter.to_s() + ": Click the 'Login' button")$stepcounter=$stepcounter + 1# if the duplicate login session pop up show upif $browser.button(:value, 'OK').exist? # Yes, pop up is here$browser.button(:value, 'OK').click # Click OK button$logger.log("Passed: Step " + $stepcounter.to_s() + ": click the 'OK' button")$stepcounter=$stepcounter + 1end# check login sucessfully or not# Watir::Wait.until {$browser.title.include?('Home')}$browser.div(:id, "content").wait_until_presentif $browser.div(:id, "content").text.include?"Home"$logger.log("Passed: Step " + $stepcounter.to_s() + ": Login successfully")$stepcounter=$stepcounter + 1else$logger.log("Failed: Step " + $stepcounter.to_s() + ": Login failed")$stepcounter=$stepcounter + 1endenddef logout_action# click the logout link$browser.link(:text, "Log Out").click$logger.log("Passed: Step " + $stepcounter.to_s() + ": Click the 'Log Out' link")# close the browser$browser.closeend生成的结果如:# Logfile created on 2012-11-21 16:17:52 +0800 by logger.rb/31641I, [21-Nov-2012 16:17:52#5096] INFO -- : Logger starting...I, [21-Nov-2012 16:17:52#5096] INFO -- :I, [21-Nov-2012 16:17:54#5096] INFO -- : ------------------------------------------I, [21-Nov-2012 16:17:54#5096] INFO -- : ## Beginning of test case 01 ##I, [21-Nov-2012 16:17:54#5096] INFO -- : ------------------------------------------I, [21-Nov-2012 16:17:55#5096] INFO -- : Passed: Step 1000: Navigate to URL:http://10.32.152.113:8080/I, [21-Nov-2012 16:17:57#5096] INFO -- : Passed: Step 1001: Enter 'test@gmail.com' in the User Name text fieldI, [21-Nov-2012 16:17:58#5096] INFO -- : Passed: Step 1002: Enter 'password123' in the Password text fieldI, [21-Nov-2012 16:17:58#5096] INFO -- : Passed: Step 1003: Click the 'Login' buttonI, [21-Nov-2012 16:17:59#5096] INFO -- : Passed: Step 1004: click the 'OK' buttonI, [21-Nov-2012 16:18:00#5096] INFO -- : Passed: Step 1005: Login successfullyI, [21-Nov-2012 16:18:00#5096] INFO -- : ------------------------------------------I, [21-Nov-2012 16:18:00#5096] INFO -- : ## End of test case 01 ##I, [21-Nov-2012 16:18:00#5096] INFO -- : ------------------------------------------ -
Watir webdriver一些常用的方法
2013-01-27 17:06:22
使用Test Unit的方式组织测试脚本,只有使用这种架构,才能进行assertrequire "test/unit"require "watir-webdriver"class TC_myTest < Test::Unit::TestCasedef testcase1$browser=selenium.Browser.new(chrome)$browser.goto('http://10.32.148.243:8080/parkinglot/')assert($browser.element(:text, 'floor Manage').click)endend最好有些常规的功能在所有的code之前,和最后运行# 在所有case运行之前进行一些操作def setup$browser = 'chrome' if $browser.nil?$site = 'http://test.localhost' if $site.nil?if $headlessrequire 'headless'$headless = Headless.new$headless.startendif $browser == 'chrome'$b = Watir::Browser.new :chromeelsif $browser == 'firefox'$b = Watir::Browser.new :ffelsif $browser == 'ie'$b = Watir::Browser.new :ieend$b.goto $siteend# 关闭所有的窗口def teardown$b.closeif $headless$headless.destroyendend通过上面的代码,会自动运行系统的default设定的浏览器,一直其设定的default的URL当然, 使用参数化的过程来选择浏览器,也更加专业:ARGV.each { |arg|if arg.downcase.include? 'chrome'$browser = 'chrome'elsif arg.downcase.include? 'firefox'$browser = 'firefox'elsif arg.downcase.include? 'ff'$browser = 'firefox'elsif arg.downcase.include? 'ie'$browser = 'ie'elsif arg.downcase.include? 'headless'$headless = trueend}我们喜欢在错误发生的时候有更详细的信息,但是不可能一直盯着跑,那就在错误发生时,拍个screenshot,以便我们回头来查看time = Time.new$b.driver.save_screenshot(File.dirname(__FILE__) + '/screenshots/' + @method_name + '_' + time.strftime('%Y%m%d_%H%M%S') + '.png');实际上,我们可以用的assert语句还有 assert_true, assert_false和assert_equal,我们判断返回值是否等于我们的期望值的时候,可以这么写:assert_equal 'Click Me', $b.text_field(:name, 'click1').value我们在组织case的时候,最好引入模块化,或者层次化,这样能搞好的整理我们的代码,例如:def form_register_page$b.text_field(:name, 'organization_name').set('Magic/More Magic')$b.text_field(:name, 'question_38').set('As mentioned above, we make magic and more magic.')$b.text_field(:name, 'question_39').set('People who like magic and more magic, as opposed to less magic.')$b.link(:id=> 'show-more').click$b.text_field(:name, 'question_41').set('Im putting stuff into question 41')$b.text_field(:name, 'question_45').set('Im putting stuff into question 45')end对于一个form里的所有操作,我们都可以封装到一个方法里,更加易读。对于time out,是个老大难问题,我们最好能够多处理一下,这里就是个很好的实例:def load_link(waittime)beginTimeout::timeout(waittime) doyieldendrescue Timeout::Error => eputs "Page load timed out: #{e}"retryendenddef browse_to_new_projectload_link(30){ $b.goto $site + "/designtourney/projects/new" }enddef click_logo_designload_link(30){ $b.link(:class, 'logo-design').click }end虽然默认的log已经足够使用,但是可读性不是很好,我们可以设置一个更加丰富的log文件来处理error:module Testmodule Unitclass TestSuitealias :old_run :rundef run(result, &progress_block)old_run(result, &progress_block)File.open('errors.log', 'w'){|f|result.faults.each{|err|case errwhen Test::Unit::Error, Test::Unit::Failuref << err.test_namef << "\n"#not in log filewhen Test::Unit::Pending, Test::Unit::Notification, Test::Unit::Omissionend}}endendendend有时候,尽管出错了,但是错误的原因完全是个意外,甚至是不可知的,我们想再试试怎么办:# create string of all argsargs = ""ARGV.each { |arg| args+=" "+arg }f = File.open("errors.log") or die "Unable to open file..."# start with an empty arrayerrors=[]f.each_line {|line|errors.push line}if errors.length > 0puts 'Attempting to resolve errors'try = 1while try <= 3puts "Try number: "+try.to_serrors.each_with_index{|name, i|test = /(.+?)\((.+?)\)/.match(name)if system "ruby \""+test[2]+".rb"+args+"\""errors[i] = falseend}errors.delete(false)if errors.length == 0puts 'All errors resolved successfully!'breakendtry+=1endFile.open('errors.log', 'w'){|f|errors.each{|error|f << errorf << "\n"}}if errors.length != 0puts 'Errors unresolved'endelseputs 'There are no errors in errors.log'end我们等于要把log中的error都再次过滤一下,从而使得有些可以避免的error跑到我们的视野中,搞的我们花费了大量时间去处理一个完全的孤立的意外。把上面所有的都整理起来,就是一个你可以作为参考的良好实例:require "rubygems" gem "test-unit" require "test/unit" require "watir-webdriver" # check arguments for browser or headless specification ARGV.each { |arg| if arg.downcase.include? 'chrome' $browser = 'chrome' elsif arg.downcase.include? 'firefox' $browser = 'firefox' elsif arg.downcase.include? 'ff' $browser = 'firefox' elsif arg.downcase.include? 'ie' $browser = 'ie' elsif arg.downcase.include? 'headless' $headless = true end} module Test module Unit class TestSuite alias :old_run :run def run(result, &progress_block) old_run(result, &progress_block) File.open('errors.log', 'w'){|f| result.faults.each{|err| case err when Test::Unit::Error, Test::Unit::Failure f << err.test_name f << "\n" #not in log file when Test::Unit::Pending, Test::Unit::Notification, Test::Unit::Omission end } } end end end end class TestExample < Test::Unit::TestCase # setup is run before every test def setup $browser = 'chrome' if $browser.nil? $site = 'http://test.localhost' if $site.nil? if $headless require 'headless' $headless = Headless.new $headless.start end if $browser == 'chrome' $b = Watir::Browser.new :chrome elsif $browser == 'firefox' $b = Watir::Browser.new :ff elsif $browser == 'ie' $b = Watir::Browser.new :ie end $timeout_length = 30 load_link($timeout_length){ $b.goto $site } end # teardown is run after every test def teardown # take screenshot of end of test, useful for failures/errors time = Time.new $b.driver.save_screenshot(File.dirname(__FILE__) + '/screenshots/' + @method_name + '_' + time.strftime('%Y%m%d_%H%M%S') + '.png'); $b.close if $headless $headless.destroy end end def browse_to_new_project load_link($timeout_length){ $b.goto $site + "/designtourney/projects/new" } end def click_logo_design load_link($timeout_length){ $b.link(:class, 'logo-design').click } end def form_fill_first_page $b.text_field(:name, 'organization_name').set('Magic/More Magic') $b.text_field(:name, 'question_38').set('As mentioned above, we make magic and more magic.') $b.text_field(:name, 'question_39').set('People who like magic and more magic, as opposed to less magic.') $b.link(:id=> 'show-more').click $b.text_field(:name, 'question_41').set('Im putting stuff into question 41') $b.text_field(:name, 'question_45').set('Im putting stuff into question 45') end def first_page_asserts type = 'regular' assert_equal 'Magic/More Magic', $b.text_field(:name, 'organization_name').value assert_equal 'As mentioned above, we make magic and more magic.', $b.text_field(:name, 'question_38').value assert_equal 'People who like magic and more magic, as opposed to less magic.', $b.text_field(:name,'question_39').value assert_equal 'Im putting stuff into question 41', $b.text_field(:name,'question_41').value end def wait_for_ajax $b.div(:id, 'ajax-loader').wait_while_present end def load_link(waittime) begin Timeout::timeout(waittime) do yield end rescue Timeout::Error => e puts "Page load timed out: #{e}" retry end end def test_save_for_later browse_to_new_project click_logo_design form_fill_first_page $b.link(:class, 'save').click wait_for_ajax assert_true $b.div(:id, 'fallr').visible? browse_to_new_project $b.div(:id, 'fallr').wait_until_present $b.wait_until{ $b.execute_script('return $(\'#fallr-wrapper\').is(\':animated\')') == false } sleep 0.5 $b.link(:id, 'fallr-button-yes').click $b.div(:id, 'fallr-overlay').wait_while_present # These assertions make sure the stuff for the first page is still all there first_page_asserts end end
-
Watir Webdriver处理新pop up的窗口
2013-01-27 16:54:08
其实很简单,就是使用该窗口即可,就是切换activity而已。下面的实例就是,切换到新的弹出窗口:annoying popup上,然后关闭这个窗口
browser.window(:title => "annoying popup").use dobrowser.button(:id => "close").clickend更多的操作方式是在windows switch里描述的:通过URL来定位窗口, 下例url为'/closable.html'w = browser.window(:url => /closeable\.html/).use通过title来定位:w = browser.window(:title => "closeable window").use通过index定位:w = browser.window(:index => 1).use关闭窗口:browser.window(:title => "closeable window").close返回titletitles = browser.windows.map { |e| e.title }titles.size.should == 2titles.sort.should == ["window switching", "closeable window"].sort -
Watir Webdriver对下载的处理
2013-01-27 16:41:03
Watir Webdriver可以对下载进行处理,最简单的方式不是对其进行管理,而是沉默处理,压根不弹出窗口才最合适。在ff中,处理的方式是:download_directory = "#{Dir.pwd}/downloads"download_directory.gsub!("/", "\\") if Selenium::WebDriver::Platform.windows?profile = Selenium::WebDriver::Firefox::Profile.newprofile['browser.download.folderList'] = 2 # custom locationprofile['browser.download.dir'] = download_directoryprofile['browser.helperApps.neverAsk.saveToDisk'] = "text/csv,application/pdf"b = Watir::Browser.new :firefox, :profile => profile这样,所有的下载文件直接就到指定的文件夹里,而且是隐式处理,同理chrome也可以这样处理:download_directory = "#{Dir.pwd}/downloads"download_directory.gsub!("/", "\\") if Selenium::WebDriver::Platform.windows?profile = Selenium::WebDriver::Chrome::Profile.newprofile['download.prompt_for_download'] = falseprofile['download.default_directory'] = download_directoryb = Watir::Browser.new :chrome, :profile => profile获取更多的option信息,ff可以在空白页面里查看:about:config -
Watir Webdriver对browser的certificate的操作
2013-01-27 16:26:35
对于弹出非trust的certificate进行处理ie,可以当alert窗口进行直接处理对于ff来说,直接屏蔽即可profile = Selenium::WebDriver::Firefox::Profile.newprofile.assume_untrusted_certificate_issuer = falseb = Watir::Browser.new :firefox, :profile => profile同理 chrome也可以屏蔽,不做验证Watir::Browser.new :chrome, :switches => ['--ignore-certificate-errors'] -
Watir WebDriver对于用户验证的处理
2013-01-27 16:13:53
有一些网站,在访问的时候,就会先弹出一个验证窗口,要求你输入用户名和密码。一般来说,常见的网站都是采用默认的验证方式,也就是说,你只需要在URL里附带user/passwod就行,例如:require 'watir-webdriver'b = Watir::Browser.new :firefoxb.goto 'http://admin:password@192.168.0.1'但是,有些网站采用更严格的验证方式,比如NTLM,这时候需要验证的是proxy。简单的快速解决方法,就是手工的进入该网站一次,这样验证方式就存在当前的profile里了,对于ff来说,就直接使用默认的profile即可:require 'watir-webdriver'b = Watir::Browser.new :firefox, :profile => 'default'b.goto 'http://192.168.0.1'当然复杂的也不会太复杂,就安装一个addon就可以解决:profile = Selenium::WebDriver::Firefox::Profile.from_name 'WatirWebDriver'profile.add_extension 'autoauth-2.1-fx+fn.xpi'b = Watir::Browser.new :firefox, :profile => profileb.goto 'http://192.168.0.1'总之,基本策略就是:1. 使用ff的profile manager创建一个profile2. 把需要的验证信息,加入profile3. 在测试中,指定使用这个profile4. 添加AutoAuth插件,一劳永逸这个方法 目前也就对ff和chrome可用,ie的话,手工配置吧 -
Watir WebDriver的wait应用
2013-01-27 15:59:00
watir提供的wait方法有:Watir::Wait.until { ... }:等待你指定的block出现变为trueobject.when_present.set:当对象出现时,你可以做点你set的什么object.wait_until_present:纯粹的等待对象的出现,什么都不干object.wait_while_present:纯的等待直到对象消失对于动态页面来说,尤其是有很多Ajax代码的页面,需要使用wait来进行等待,但是watir的wait是等待页面完成之后就结束了,实际上Ajax很多代码要在页面load之后还在加载,这样的话,就导致等待无效,真正有效的是Wait_while_present我们可以自己添加一个简单的方法进行处理:def wait_for_ajaxbrowser.div(:id, 'ajax-loader').wait_while_presentend默认的等待时间是30秒,你可以通过参数去设定时间长度:b.select_list(:id => 'entry_1').wait_until_present(100)等待一百秒下面是一些简单的例子require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'b.select_list(:id => 'entry_1').wait_until_presentb.text_field(:id => 'entry_0').when_present.set 'your name'b.button(:value => 'Submit').clickb.button(:value => 'Submit').wait_while_presentWatir::Wait.until { b.text.include? 'Thank you' }你甚至可以使用 Implicit waits 来设置最长的等待时间,例如:require 'watir-webdriver'b = Watir::Browser.newb.driver.manage.timeouts.implicit_wait = 3 #3 secondsimplicit wait的最长的等待时间意味着3秒之内,只要找到对象,就不再等待,直接进入下一步,而wait,要一直等到规定的时间超时才进行下一步。
标题搜索
我的存档
数据统计
- 访问量: 151216
- 日志数: 185
- 文件数: 6
- 建立时间: 2007-08-06
- 更新时间: 2015-01-06