-
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,要一直等到规定的时间超时才进行下一步。 -
Watir WedDriver支持的浏览器操作
2013-01-27 15:26:02
Watir Webdriver的工作方式是:Watir 脚本 -> Browser Driver -> Browser ->Screen (or headless)目前支持的Browser Driver 有IEDriver, ChromeDriver, FFDriver,SafariDriver, HeadlessDriver我们来看一下对各个browser的支持:Chrome标准创建browser = Watir::Browser.new :chrome带profile的创建,下例为设置不弹出download窗口,并设置下载默认路径profile = Selenium::WebDriver::Chrome::Profile.newprofile['download.prompt_for_download'] = falseprofile['download.default_directory'] = "/path/to/dir"browser = Watir::Browser.new :chrome, :profile => profile带switch的创建,下例为忽略证书错误,关闭popup窗口弹出,禁止自动翻译browser = Watir::Browser.new :chrome, :switches => %w[--ignore-certificate-errors --disable-popup-blocking --disable-translate]设置代理browser = Watir::Browser.new :chrome, :switches => %w[--proxy-server=myproxy.com:8080]Firefox标准创建browser = Watir::Browser.new :firefox使用默认的profilebrowser = Watir::Browser.new :firefox, :profile => 'default'设置所需的profileprofile = Selenium::WebDriver::Firefox::Profile.newprofile['browser.download.dir'] = "/tmp/webdriver-downloads"profile['browser.download.folderList'] = 2profile['browser.helperApps.neverAsk.saveToDisk'] = "application/pdf"browser = Watir::Browser.new :firefox, :profile => profile禁止native event,该方法主要是window使用默认的一个较低level的交互方法来与webdrive进行交互,有时候会引起莫名的错误,可以禁止:profile = Selenium::WebDriver::Firefox::Profile.newprofile.native_events = falsebrowser = Watir::Browser.new :firefox, :profile => profile设置代理服务profile = Selenium::WebDriver::Firefox::Profile.newprofile.proxy = Selenium::WebDriver::Proxy.new :http => 'myproxy.com:8080:, :ssl => 'myproxy.com:8080'browser = Watir::Browser.new :firefox, :profile => profile设置使用addon,例如:firebugprofile = Selenium::WebDriver::Firefox::Profile.newprofile.add_extension "../path/to/firebug.xpi"browser = Watir::Browser.new :firefox, :profile => profileIE标准创建browser = Watir::Browser.new :firefoxIE使用默认配置,也就是说,你必须手工配置IE来满足你的需要。Safari标准创建browser = Watir::Browser.new :safarisafari现在支持还不够完善,有待进一步更新。HeadlessHeadless不是浏览器,他是ruby对Xvfb的封装,从而使得能headless的linux上运行一个图形界面程序。举个简单的例子require 'watir-webdriver'require 'headless'headless = Headless.newheadless.startbrowser = Watir::Browser.start 'www.google.com'puts b.titleb.closeheadless.destroy通过先生成headless,我们就能在非图形界面的操作系统上,运行一个图形界面的程序。 -
Watir Webdriver的mobile测试
2013-01-27 14:52:21
实际上,Watir Webdriver可以测试的仍然是Site,不是APP,可以用于测试mobile device访问的mobile site的方式有三种:1. 在一个真实的设备上,利用内置的browser进行测试2. 在一个仿真的设备上,利用内置的browser进行测试3. 在pc机上,将browser设置为mobile browser进行测试对于在真实设备上进行测试,速度会相对比较慢,而且iOS还需要额外的费用。而且还需要在iOs和Android的设备上先安装相应的driver。所以最简单的方法,就是利用webdriver-user-agent gem 进行模拟测试方法如下:require 'watir-webdriver'require 'webdriver-user-agent'driver = UserAgent.driver(:browser => :chrome, :agent => :iphone, :orientation => :landscape)browser = Watir::Browser.new driverbrowser.goto 'tiffany.com'browser.url.should == 'http://m.tiffany.com/International.aspx'我们可以看到,最关键的就是:driver = UserAgent.driver(:browser => :chrome, :agent => :iphone, :orientation => :landscape)这个步骤,指明了你要使用的浏览器类型,仿真器类型,和视图方式。该gem可以支持的浏览器有ff和chrome可以支持的仿真器类型有:iphone, ipad, android_phone, 和android_tablet支持的视图方式有两种,水平和竖直。 (portrait and landscape) -
Selenium Webdriver和Watir Webdriver的详细对比
2013-01-27 14:33:13
我们来看一些具体的API,来对Selenium的WebDriver和Watir的Webdirver进行更加直观的了解1. 生成一个新的browserSelenium:driver=Selenium::WebDriver.for:firefoxWatir:driver=Watir::Browser.new:firefox2. 转向指定的页面Selenium:driver.get 'http://www.baidu.com'Watir:driver.goto 'http://www.baidu.com'3. 查找元素Selenium:element = driver.find_element(:id, "coolestWidgetEvah")Watir:element = driver.element(:id, "coolestWidgetEvah")4. 执行一段js代码Selenium:element = driver.execute_script("return $('.cheese')[0]")Watir:element = driver.fire_event("return $('.cheese')[0]")5. Alert 操作Selenium:alert = driver.switch_to.alertalert.okWatir:browser.alert.ok -
Selenium Web Driver 和 Watir Web Driver
2013-01-27 14:19:25
Watir Webdriver不仅仅是Watir的升级,实际上它是对Selenium WebDriver的API进行的二次封装。从而使得其API能够更好的符合Ruby语言的规范,和更加简单明确的方法应用。可以这么说,如果使用Ruby进行WebDriver开发,Watir WebDriver更加好用,更加简洁,更加利于新手的学习。我们看一下Watir Webdriver和Selenium Webdriver的一个简单例子,从而更好的理解封装后的简便化:Selenium WebDriverrequire 'rubygems'require 'selenium-webdriver'driver = Selenium::WebDriver.for :firefoxdriver.get "http://google.com"element = driver.find_element :name => "q"element.send_keys "Cheese!"element.submitWatir WebDriverrequire 'rubygems'require 'watir-webdriver'driver = Watir::Browser.new:firefoxdriver.goto "http://google.com"element = driver.element(:name => "q")element.send_keys "Cheese!"element.submit从上面的语法可知,我们能从Watir Driver中明确的知道,我们要创建一个Browser的实例,指定browser的type。比selenium的构造方法更加明了。我们转向到想去的页面,goto比get更加容易理解。我们根据element的name在页面上查找,可以随时改变属性为:id,:value等等,更加便于我们二次封装。其他的基本上类似。所以说,在功能一致的情况下,一个更加便于阅读和理解的代码,对于初学者来说 更加容易去掌握,这更加符合ruby语言当初的初衷:让学习开发语言不再痛苦。 -
Selenium 2 简介
2013-01-27 14:17:09
selenium2 包括两部分:Selenium IDE和Selenium WebDriver。Selenium IDE作为Firefox的一个插件,以录制-回放的方式进行工作。主要是用来快速创建一个bug的回归测试脚本,或者辅助自动化测试来进行一下探索性测试。而Selenium WebDriver主要是用于创建一个强大而稳定的基于浏览器的回归测试的测试集。 甚至能够进行多种测试环境中进行分布式测试。Selenium WebDriver是RC的继承者,虽然在Selenium2中,我们仍然可以使用Rc,但是实际上,RC已经被正式的宣布不再进行更新。我们可以把Selenium2看作是如下三者的集合体:已有的RC+新的WebDriverAPI+二者共同使用的SeleniumServer。而且对于WebDriver和RC共同使用的Selenium Server在Selenium2中已经内置了网格支持。Selenium WebDriver的革新之处就是提供了一套WebDriver的API,从而使得我们可以在本地服务器或者远程服务器上像真实用户那样对浏览器进行操作。Selenium WebDriver的出现的目的是为了解决RC的一些限制。从而能够更好的支持动态页面,例如在不重载页面就更新了该页面一些元素的情况。Selenium WebDriver是通过直接调用浏览器的自有方法来达到驱动浏览器的目的。而RC的方式是将javaScript代码“注入”到当前的浏览器中,当浏览器加载时,就将“注入”的javascript代码同时加载,从而这些“注入”的javascript驱动浏览器进行各种行为。简而言之,webdriver是直接驱动浏览器本身,RC是通过我们植入的代码,间接的驱动浏览器。如果你的Webdriver目的就是对一套固定的测试环境进行测试,是不需要使用Selenium Server的。只有在如下的情况下,才会使用:1. 使用selenium-Grid将你的测试用例分布在不同的测试环境中。2. 你需要连接一个remote server来使用你当前环境下没有的浏览器。3. 你不想使用Java bindings(Python,C#或者Ruby),而是想使用HTMLUnitDriver的时候。 -
Cucumber的tags
2013-01-25 13:55:01
cucumber提供了一个简单有效的标志方法来帮你更好的整理你的测试流程使用起来很简单,只需要在代码行上加上你想要的标志,例如在login.feature里添加@init, @major, @authenticate:@init Feature: . . . . . . @major @authenticate Scenario: A user should authenticate before accessing any resource. Given I do have a user named "testuser" When the user visits the login page And the user named "testuser" authenticates successfully Then I should see . . . . . .
比如,我们想做一个初始化的流程的测试,只需要运行那些带有@init的feature即可:$ cucumber --profile=my_profile --tags=@init也可以用:$ rake cucumber:init或者指定功能区域的authenticate相关的场景$ cucumber --profile=my_profile --tags=@authenticate features/login或者指定的功能级别:$ cucumber --tags=@major #没有指定profile的时候,会使用默认的profile对于rake的方式来说,想使用指定的tag,必须在config/cucumber.yml中预先定义才行。我们也可以指定执行@tag的index,例如下例就是指执行拥有第三个@major标签的场景$ cucumber --tags=@major:3 features/log我们可以使用反选符号~,例如下例就是指执行除了含有@major标签的场景之外的所有场景$ cucumber --tags=~@major features/log -
Cucumber的 Step Definitions
2013-01-25 13:09:09
Cucumber的step deifne过程,就是通过正则表达式,将feature里的文本步骤转义为可执行的代码。好的步骤定义的标准是:匹配串尽可能的短最好能同时匹配正向和反向的条件一个步骤最多两个匹配参数用于匹配的参数名称最好简单明确每个步骤最好不要超过10行的处理,否则进行二次封装最好不要去引用其他的步骤在step define文件中,没有次序的概念,Given,When,Then可以任意行定义。甚至结构也可以是嵌套的, 例如:Given /some "(.*)" action/ do |act|. . .endThen /some "(.*)" action/ do |act|. . .endWhen /in an invoiced non-shipped situation/ doGiven "some \"invoiced\" action"Then "some \"non-shipped\" action". . .end我们在上例可以看到, 在when里直接包含了已经预先定义好的Given和Then,这是允许的。但是最好不要这么做。事实上我们为了避免这种看起来很混淆的代码,我们一般用step来取代这些关键词,上面的例子就变为了:When /in an invoiced non-shipped situation/ dostep("some \"invoiced\" action")step %Q("some \"non-shipped\" action"). . .end%Q是用来去除多余的“符号对于多个step,我们可以用steps块来定义, 上例就变为:When /in an invoiced non-shipped situation/ dosteps %Q{Given "some \"invoiced\" action"Then "some \"non-shipped\" action". . .}end -
Cucumber的关键字
2013-01-25 12:44:59
Cucumber的关键字有如下Feature 功能Background 背景Scenario 场景Scenario outline 场景大纲Example 例子Given 假如And 而且....Then 当And 并且...But 但是And 并且...Then 那么And 并且...我们在Step里面没有对应feature, Scenario, Scenario outline, example的匹配定义。对应background的是:Before doendAfter doendBefore会在所有的场景前执行,After会在所有的场景后执行Given对应的是“假如”(Given)和“并且”(Given后的And)Given / / do |action|endBut对应的是“但是”(But)和“而且”(But后的And)But / / do |action|endWhen对应的是“当”(When)和“而且”(When后的And)When // do |op|endWhen对应的是“那么”(Then)和“而且”(Then后的And)Then // do |result|end -
Cucumber的目录结构
2013-01-25 12:26:35
Cucumber的目录结构很简单,我们举个简单的例子:features ├── step_definitions └── support └── env.rb
你所定义的feature文件位于features目录下,你可以定义多个feature文件,例如一个user story一个。在step_definintions目录下,定义steps的map文件在lib目录下,定义map用的自动化测试脚本然后support目录下的env.rb,用于在执行feature前,进行你的环境初始化,例如,生成一个@browser=Watir::Browser.new:chrome. 并且初始化一些全局变量。实际上,我们能够用更复杂的方式来组织我们的目录结构,只要你保证所有的文件都在features文件夹里,任意指定即可。例如,使用功能区域组织我们的目录结构:|-- features | |-- entities | | |-- entity.feature | | `-- step_definitions | | |-- anything.rb | | `-- entity_steps.rb | |-- locations | | |-- location.feature | | `-- step_definitions | | `-- location_steps.rb | |-- sites | | `-- step_definitions | |-- step_definitions | | |-- local_assert_steps.rb | | |-- local_crud_response_steps.rb | | |-- local_email_steps.rb | | |-- local_file_steps.rb | | |-- local_script_steps.rb | | |-- local_steps.rb | | |-- local_web_steps.rb ` | `-- local_xml_file_steps.rb `-- support |-- env.rb |-- local_env.rb `-- local_transforms.rb
或者用模块层次组织我们的目录结构:|-- features | |-- models | | `-- entities | | |-- entity.feature | | `-- step_definitions | | |-- anything.rb | | `-- entity_steps.rb | |-- views | | |-- entity_new | | `-- step_definitions | | `-- entity_new_steps.rb | |-- step_definitions ` | `-- local_steps.rb `-- support |-- env.rb |-- local_env.rb `-- local_transforms.rb
标题搜索
我的存档
数据统计
- 访问量: 151676
- 日志数: 185
- 文件数: 6
- 建立时间: 2007-08-06
- 更新时间: 2015-01-06