(整理)自动化测试(Cucumber+Watir)

来源:互联网 发布:大学网络课程怎么快进 编辑:程序博客网 时间:2024/06/03 18:44

为什么要实施自动化测试?

  • ##### 应用系统日趋复杂的客观要求
    一个不可避免的问题是,应用系统变得日趋复杂,并且造成了更大的风险。测试消耗的成本越来越高,花费的时间也越来越长。而成本与时间是有限的。为了在有限的成本和时间范围内,控制发布的软件产品质量,测试人员被要求在尽量短的时间内对软件进行足够的测试。
  • ##### 避免重复测试的主观要求
    迭代式开发过程:目前的软件开发或多或少的使用了迭代式开发过程,这就导致了我们需要进行一轮又一轮的测试。
    回归测试:为了检查软件版本是否正确,或者当发现软件中出现一个或多个以前曾经被修复的缺陷时,不可避免的需要进行回归性的测试。

自动化测试实施失败的因素?

  1. 期望值过高。就像管理人员要求完全测试一样,期望100%的测试自动化,也同样是一个不显示的需求。
  2. 自动化的收益和成本:
    成本 = 用例的开发 + 用例的维护
    收益 = 重复运行次数 + 节约的时间
    收益和成本

自动化测试的成长过程

Testing often begins as freestyle, expands toward scripted.
测试总是从自由式的探索开始,朝着脚本化方向扩张。
脚本化

自动化测试的应用时机

应用时机

冒烟测试
这一术语源自硬件行业。对一个硬件或硬件组件进行更改或修复后,直接给设备加电。如果没有冒烟,则该组件就通过了测试。在软件中,“冒烟测试”这一术语描述的是在将代码更改嵌入到产品的源树中之前对这些更改进行验证的过程。在检查了代码后,冒烟测试是确定和修复软件缺陷的最经济有效的方法。冒烟测试设计用于确认代码中的更改会按预期运行,且不会破坏整个版本的稳定性。

回归测试
回归测试是指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。自动回归测试将大幅降低系统测试、维护升级等阶段的成本。回归测试作为软件生命周期的一个组成部分,在整个软件测试过程中占有很大的工作量比重,软件开发的各个阶段都会进行多次回归测试。在渐进和快速迭代开发中,新版本的连续发布使回归测试进行的更加频繁,而在极端编程方法中,更是要求每天都进行若干次回归测试。因此,通过选择正确的回归测试策略来改进回归测试的效率和有效性是很有意义的。

重头戏:测试方案设计

  1. 自动化测试的功能方案设计的流程:
    操作对象→对象属性→功能实现→结果验证
  2. 自动化测试的功能方案设计操作用例图:
    用例图

Cucumber

开源项目地址:github/cucumber-ruby

Installation

you can run:

gem install cucumber-rails -v 0.3.2// gem install cucumber  

If you are using Bundler, just add it to your Gemfile:

gem "capybara", "1.1.1"gem "cucumber", "1.1.0"gem "cucumber-rails", "0.3.2"  

Then initialize a features directory:

cucumber --init   

Running

To see the full list of options:

cucumber --help    

Otherwise, to run all features:

cucumber   

If working on a fresh Rails project, first set up the (empty) database:

rake db:migrate  

(Otherwise Cucumber fails with the error no such file to load – YourProjectName/db/schema.rb.)

Then run the features:

rake cucumber

Watir WebDriver

开源项目地址:github/watir
API:api

浏览器支持:

Firefox, Chrome, IE,Safari

I could make uo lots of quotes about how good it is, but I don’t need to, it’s that good.

Getting Started

// gem install watir-webdriver  gem 'watir', '~> 6.7'gem install watir

Getting Going

require 'watir-webdriver'b = Watir::Browser.newb.goto 'bit.ly/watir-webdriver-demo'b.text_field(:id => 'entry_1000000').set 'your name'b.select_list(:id => 'entry_1000001').select 'Ruby'b.select_list(:id => 'entry_1000001').selected? 'Ruby'b.button(:name => 'submit').clickb.text.include? 'Thank you'  

自动化测试

Cucumber和Watir的关系

Cucumber是一个BDD框架;Watir是一个Web Driver。两者都是由Ruby编写的。

单独使用Cucumber

对于Cucumber的执行过程可以参考文章:行为驱动开发:Cucumber的目录结构和执行过程。
由于Cucumber和Watir没有必然联系,因此两者均可单独使用,下面就让我们用Cucumber写一个简单的单元测试。定义一个需要测试的Calculator类如下:

class Calculator  def add num1, num2    num1 + num2  endend  class Calculator  def add num1, num2    num1 + num2  endend  

用于测试的Calculator类的add方法放入feature文件如下:

Feature: Unit testforCalculatorScenario: Add two numbersGiven I have a calculator createdWhen I add '3' and '5'Then I should get the result of '8'  

对应的step文件为:

require File.join(File.dirname(__FILE__), "../calculator")require 'rspec'Given /^I have a calculator created$/ do  @calculator = Calculator.newendWhen /^I add '([^"]*)' and '([^"]*)'$/ do |num1, num2|  @result = @calculator.add(num1.to_i, num2.to_i)endThen /^I should get the result of '([^"]*)'$/ do |expected_result|  @result.should == expected_result.to_iend  

在以上的step文件中,第1,2行分别require了自定义的Calculator类和rspec(用于assertion,参考第13行的‘should’),第5行新建了一个@calculator实例变量,第9行完成两个数相加(3 + 5),第13行为测试断言。运行cucumber命令,输出结果如下:

Feature: Unit testforCalculatorScenario: Add two numbers             # features/Calculator.feature:4Given I have a calculator created   # features/step_definitions/calculator_step.rb:4WhenI add '3' and '5'              # features/step_definitions/calculator_step.rb:8ThenI should get the result of '8' # features/step_definitions/calculator_step.rb:121 scenario (1 passed)3 steps (3 passed)100m 0.002s  

单独使用Watir

接下来用Watir完成Google搜索功能,新建watir_google.rb文件并加入以下内容:

require 'watir_webdriver'  browser = Watir::Browser.new :chrome  browser.goto "www.google.com/"  browser.text_filed(:name => "q").set "Why I am so handsome ?"  browser.button(:name => "btnG").click  

当执行到第2行时,一个浏览器窗口会自动打开,之后访问google主页(第三行),设置搜索关键字“why I am so handsome ?”,最后点击搜索按钮,单独运行Watir成功。

用Cucumber + Watir写自动化测试

由上文可知,Cucumber只能用接近自然语言的方式描述业务行为,而Watir则只能对人为操作页面动作进行模拟。当使用Cucumber + Watir实现自动化测试时,通过正则表达式匹配将Cucumber的行为描述与Watir的网页操作步骤耦合起来即可。同样以Google搜索为例,搜索关键字后,我们希望获得搜索结果,先用Cucumber完成搜索行为描述:

Feature:GoogleSearch  Scenario: search for keyword  Given I am on google home page  When I search for 'why I am so handsome ?'  Then I should be able to view the search result of 'why I am so handsome ?'  

对应的Watir代码如下:

require "rubygems"  require "watir-webdriver"  require "rspec"  Given /^I am on google home page$/ do    @browser = Watir::Browser.new :chrome    @browser.goto("www.google.com")  end  When/^I search for'([^"]*)'$/ do |search_text|  @browser.text_field(:name => "q").set(search_text)  @browser.button(:name => "btnK").clickendThen /^I should be able to view the search result of '([^"]*)'$/ do |result_text|  @browser.text.should include(result_text)end  

运行cucumber,一个新的浏览器被打开,显示结果测试通过

自动化测试的设计模式 :Page对象

在上面的例子中,所有的step均直接对@browser对象进行操作,对于这样简单的例子并无不妥,但是对于多页面的网站来说显得过于混乱,既然是面向对象,我们就希望将不同的页面也用对象封装,于是引入Page对象,即可完成对页面的逻辑封装,又实现了分层复用。此时位于high-level的Cucumber文件无需改动,我们只需要定义一个Page对象来封装Google页面:

require "rubygems"  require "watir-webdriver"  require "rspec"  class GooglePage    def initialize      @browser = Watir::Browser.new :chrome      @browser.goto("www.google.com")    end    def search str      @browser.text_filed(:name =>   "q").set(str)      @browser.button(:name => "btn").click    end    def has_text text      @browser.text.should include(text)    endend  

相应的step文件需要做相应的修改:

require File.join(File.dirname(_FILE_), "google-page")  Given /^I am on google home page$/ do    @page.GooglePage.new  end  When /^I search for '([^"]*)'$/ do |search_text|    @page.search search_text  end  Then /^I should be able to view the search result of '([^"]*)'$/ do |reault_text|    @page.has_text result_text  end  

运行cucumber,一个新的浏览器被打开,显示结果与上次结果相同。

加入用户角色

既然是行为驱动,既然是模拟用户实际操作,name直接对Page对象进行操作也显得不够,于是我们引入用户角色User,对于拥有多种用户角色的网站来说特别实用。加入User对象之后,step文件中不再出现对Page对象的直接引用,而是在User对象的行为方法中进行引用,定义User对象如下:

require File.join(File,dirname(_FILE_), "google-page")  class User    def initialize      @browser = Watir::Browser.new :chrome    end    def visit_google      @page = GooglePage.new(@browser)    end    def search_text text      @page.search text    end    def assert_text_exist text      @page.has_text text    end  end  

feature文件保持不变,在step文件用User代替Page:

require File.join(File.dirname(_FILE_), "user")  Given /^I am on google home page$/ do    @user = User.new    @user.visit_google  end  When /^I search for '([^"])'$/ do |search_text|    @user.search_text search_text  end  Then /^I should be able to view the search result of '([^"])'$/ do |result_text|    @user.assert_text_exist result_text  end  

运行cucumber,一个新的浏览器被打开,显示结果与上次结果相同。
对于拥有多个用户角色的网站,比如有admin,user等,可分别对这些角色定义相应的对象,再在step文件中应用这些对象即可。

用ruby的Module来封装不同的行为功能

对于单个用户来说,比如网上购物网站的customer,既要购物操作,又能修改自己的profile,此时为了对这些不同的逻辑功能进行组织,可引用ruby中的Module来进行分装,即将customer的不同行为功能模块封装在不同的Module中,然后customer对象中include这些Module。为了简单起见,仍然用Google搜索进行演示,此时可将搜索功能加入到Module中,定义搜索module如下:

module SearchBehavior    def visit_google      @page = GooglePage.new(@browser)  end    def search_text text      @page.search text    end    def assert_text_exist text      @page.has_text text    endend  

在User对象中include这个Module:

require File.join(File.dirname(__FILE__), "search-behavior")  class User  require 'search_behavior'    def initialize      @browser = Watir::Browser.new :chrome    endend  

对step文件和feature文件均不用修改,运行cucumber,一个新的浏览器被打开,显示结果与上次结果相同。

总结

我们可以在Cucumber对应的step文件中直接访问Watir的API,这样的确也能达到测试目的,但这样的缺点在于缺少设计,于是我们引入Page对象来封装不同的页面,引入用户角色管理不同的用户行为,再引入Module来组织不同的功能模块,最后重构成了一个简单实用的自动化测试框架。

Web对象的识别-Watir-WebDriver

简介

input-输入框  button-按钮  select-下拉框  link-链接  click-点击  radio-单选  checkbox-多选  send_key :enter-模拟键盘  

页面元素的捕获

页面元素

  • attribute_value
    获取当前空间的属性

    Value = ie.link(:id=>'xxx').attribute_value("href")
  • rand_select
    随机选择select list中的某一项

    ie.select_list(:name=>’’).rand_select
  • popupwin
    端架弹窗上的‘确定’按钮

    ie.popupwin.button(:name=>"确定").click
  • sikuli_image
    点击图片控件

    ie.sikuli_image(:image=>"1.png").click  ie.sikuli_image(:image=>"1.png;2.png").click#可以指定多张图片来识别
  • double_click
    双击事件

    ie .sikuli_image(:image=>"1.png").double_click
  • right_click
    右击事件

  • exist?
    判断用户元素是否存在

    edit = ie.text_field(:name,”username”)
    if edit.exist?() #The highlighted
    edit.flash
    ie.text_field(:name, “password”).set(pwd)
    ie.button(:class, “x-login-submit”).click
    end
    end

  • Text Fields

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo't = b.text_field :id => 'entry_0't.exists?t.set 'your name't.value
  • Select LIsts - Combos

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo's = b.select_list :id => 'entry_1's.select 'Ruby's.selected_options# 返回选中的选项  puts b.select(:name, 'sex').selected_options  
  • Radios

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'r = b.label(:text => 'What is ruby?').parent.radio :value => 'A gem'r.exists?r.setr.set?
  • Checkboxes

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'c = b.label(:text => 'What versions of ruby?').parent.checkbox :value => '1.9.2'c.exists?c.setc.set?# 选择checkbox  b.checkbox(:name, 'check_me').set true # 清除选择  b.checkbox(:name, 'check_me').set false 
  • Buttons

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'btn = b.button :value, 'Submit'btn.exists?btn.click
  • Links

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'l = b.link :text => 'Google Docs'l.exists?l.click
  • Div & Spans

    require 'watir-webdriver'b = Watir::Browser.start 'bit.ly/watir-webdriver-demo'd = b.div :class => 'ss-form-desc ss-no-ignore-whitespace'd.exists?d.texts = b.span :class => 'ss-powered-by's.exists?s.text  

实例

按钮

ie.button(:name=>"",:id=>"",:index=>n,:type=>"").click    ie.button(:name=>"",:id=>"",:index=>n,:type=>"").doclick

输入框

ie.text_field(:name=>"").set "变量"   ie.text_field(:name=>"").value 取text_field值不是用text而是value!

下拉框

ie.select_list(:name=>"").select "下拉框值"  ie.select_list(:name=>"").select "#1" #表示第一项内容  ie.select_list(:name=>"").rand_select  ie.select_list(:name=>"").getSelectedItems|getAllContents->返回Array

单选框

ie.radio(:id=>"",:name=>"",:index=>n).set(选中当前radio)  ie.radio(:id=>"",:name=>"",:index=>n).clear(取消选中当前radio)ie.div(:class=>"iradio_minimal-blue checked").radios[1]

复选框

ie.check_box(:id=>"",:name=>"",:index=>n).set(true|false)(true表示选中,false表示不选中)  ie.check_box(:id=>"",:name=>"",:index=>n).clear(取消选中当前checkbox)

链接

ie.link(:text=>"").click/doclick  ie.link(:text=>"").href(返回当前link指向的链接)

cell (TD标签,用时一般需要先找到上层控件如table、div等)

ie.table(:class=>"",:index=>n).cell(:class=>"",:index=>n).text  ie.table(:index=>n).rows 行  列 .text (行、列从1开始)  ie.div(:class=>"",:index=>n).cell(:class=>"",:index=>n).text

span

ie.table(:id=>"").span(:class=>"").text

弹出框

ie.popupwin.get_static_text (返回当前提示框的文本)   ie.popupwin.button(:name=>"确定").click/doclick (前一个点击按钮必须用doclick)   ie.file_dialog(:index=>1/2).set_file(file_path_download,true) (保存文件的弹出窗口)  

alert

if b.alert.exists?      puts "ok"else    puts "no"endputs b.alert.text #b.alert.ok b.alert.close  b.close  

图片

ie.image(:src=>/word3a_nor.gif/).click/doclick

back

后退

ie.back

forward

前进

ie.forward

refresh

刷新页面

ie.refresh

在Watir-WebDriver中处理frame是非常简单的,就跟处理其他页面元素一样:

b.frame(:id => "content_ifr").send_keys "hello world"

素材来源:

  • 10分钟学会自动化测试框架–Cucumber + Watir

  • 基于Ruby的Watir-WebDriver自动化测试框架

原创粉丝点击