发布新日志

  • Watir Web-Driver Element 的通用方法(2)

    2013-01-15 17:48:24

    - (Object) flash

    修改的元素背景颜色,从而高亮该元素。

    browser.div(:id => "draggable").flash

     

    - (Object) focus

    focus置于指定的元素之上。

    browser.div(:id => "draggable").focus

     

    - (Object) focused?

    判断指定元素是否已经被focused。注意,不是所有元素都支持该方法,不支持的一律返回false

    browser.div(:id => "draggable").focused?

     

    - (Object) hash

    返回指定元素的哈希值

    browser.div(:id => "draggable").hash

     

    - (Object) hover

    将鼠标移至指定元素的中间位置。并非所有的浏览器都支持该操作。

    browser.div(:id => "draggable").hover

     

    - (String) html

    返回该元素所包含的HTML编码。

    browser.button(:name, “btnK”).html
    #=> “<input value =\”Google Search\” name=\”btnK\” type=\”submit\” jsaction=\”sf.chk\” >”

     

     

     

    - (Object) inspect

    返回指定元素对象的检查信息。

    browser.button(:name, “btnK”).inspect
    #=> “#<Watir::Button:0x..f8cf0d9 located =false selector={:name=>\”btnK\”, :tag_name=>\”button\”}>”

     

    - (Object) parent

    返回指定元素对象的父元素。

    browser.button(:name, “btnK”).parent
    #=> #<Watir::HTMLElement:0x16d0c6ce located=false selector={:element=>(webdriverelement)}>

     

    - (Boolean) present?

    指定元素是否在页面上存在并显示。

    browser.button(:name, “btnK”).present?
    #=> true

     

    - (Object) right_click

    右键点击指定元素。并非所有的浏览器都支持该方法。

    browser.button(:name, “btnK”).right_click

     

    - (Object) run_checkers

    返回指定元素对象的包含的checker列表

    Check_for_bad_things = Proc.new do
    puts “Server Error!” if @browser.text.include?(Server error”)
    end
     
    @browser.add_checker(Check_for_bad_things)
    @browser.run_checkers
    #=> [#<Proc:0x1053a50@(irb):69>]
  • Watir Web-Driver Element 的通用方法(1)

    2013-01-15 17:47:16

    - (Object) == (other) (also: #eql?)

    返回是否两个element是相同的。要注意的是是两个Object相同,不是相等。

    browser.a(:id => "foo") == browser.a(:id => "foo")

    #=> true

     

    - (String) attribute_value(attribute_name)

    返回元素的指定属性的值。

    browser.a(:id => "foo").attribute_value ("href")
    #=> "http://watir.com"  

     

    - (Watir::Browser) browser

    返回当前的browser

    browser.browser
    #=> #<Watir::Browser:0x..fa487467e url=”http://www.ggogle.com.hk/”  title=”Google”>

     

    - (Object) click(*modifiers)

    点击当前元素,同时可以提供可选值用于复杂的点击情况,例如Ctrl+click

    element.click
    element.click(:shift)
    element.click(:shift, :control)

    可支持的可选值有::shift, :alt, :control, :command, :meta

    - (Object) double_click

    双击当前元素。

    browser.a(:id => "foo").double_click

     

     

     

    - (Object) drag_and_drop_by(right_by, down_by)

    拖拽元素对象到指定的位置。位置指定为目标区域的右边线和下边线。

    browser.div(:id => "draggable").drag_and_drop_by (100, -200)

     

    - (Object) drag_and_drop_on(other)

    拖拽元素对象到指定的对象上。例如从左边列表拖拽一个选项到右边列表中。

    a = browser.div(:id => "draggable")
    b = browser.div(:id => "droppable")
    a.drag_and_drop_on(b)

     

    - (Object) driver

    元素对象的driver信息。

    browser.driver
    #=> #<Selenium::WebDriver::Driver:0x67382c7e browser=:chrome>

     

    - (Boolean) exists? (Also known as: exist?)

    判断元素对象是否存在。

    browser.a(:id => "foo") .exists?

    #=> true

     

    - (Object) fire_event(event_name)

    在指定的元素上执行一个JavaScript事件。

    browser.a(:id => "foo").fire_event(:click)
    browser.a(:id => "foo").fire_event("mousemove")
    browser.a(:id => "foo").fire_event "onmouseover"
  • 如何在 Watir 中识别各种 HTML 对象(7)

    2013-01-14 17:55:27

    Table元素

    HTML tag - <table>

     

    可以用来定位的方法有:

    Browser.table(:class,  “table class”)

    Browser.table(:id,  “table id”)

    Browser.table(:index,  0)

    Browser.table(:name,  “table name”)

    Browser.table(:text,  “table text”)

    Browser.table(:xpath,  “table xpath”)

    Browser.table(:class=>“table class name”,  :index, 0) (Mulitple Attributes)

     

    Text_field元素

    HTML tag - <input type=”password”>/<input type=”text”>

     

    可以用来定位的方法有:

    Browser.textfield(:class,  “textfield class”)

    Browser.textfield(:id,  “textfield id”)

    Browser.textfield(:index,  0)

    Browser.textfield(:name,  “textfield name”)

    Browser.textfield(:text,  “textfield text”)

    Browser.textfield(:value, “textfield value”)

    Browser.textfield(:xpath,  “textfield xpath”)

    Browser.textfield(:class=>“textfield class name”,  :index, 0) (Mulitple Attributes)

    Textarea元素

    HTML tag - <textarea>

     

    可以用来定位的方法有:

    Browser.textarea(:class,  “textarea class”)

    Browser.textarea(:id,  “textarea id”)

    Browser.textarea(:index,  0)

    Browser.textarea(:name,  “textarea name”)

    Browser.textarea(:text,  “textarea text”)

    Browser.textarea(:value, “textarea value”)

    Browser.textarea(:xpath,  “textarea xpath”)

    Browser.textarea(:class=>“textarea class name”,  :index, 0) (Mulitple Attributes)

     

    ul元素

    HTML tag - <ul>

     

    可以用来定位的方法有:

    Browser.ulist(:class,  “ulist class”)

    Browser.ulist(:id,  “ulist id”)

    Browser.ulist(:index,  0)

    Browser.ulist(:name,  “ulist name”)

    Browser.ulist(:text,  “ulist text”)

    Browser.ulist(:xpath,  “ulist xpath”)

    Browser.ulist(:class=>“ulist class name”,  :index, 0) (Mulitple Attributes)

     

    New Browser Windows 对象

    可以用来定位的方法有:

    Browser.window(:href,  “new window href”)

    Browser.window(:title,  “new window title”)

  • 如何在 Watir 中识别各种 HTML 对象(6)

    2013-01-14 17:54:40

    Radio元素

    HTML tag - <input type=”radio”>

     

    可以用来定位的方法有:

    Browser.radio(:class,  “radio class”)

    Browser.radio(:id,  “radio id”)

    Browser.radio(:index,  0)

    Browser.radio(:name,  “radio name”)

    Browser.radio(:text,  “radio text”)

    Browser.radio(:value, “radio value”)

    Browser.radio(:xpath,  “radio xpath”)

    Browser.radio(:class=>“radio class name”,  :index, 0) (Mulitple Attributes)

     

    Tr元素

    HTML tag - <tr>

     

    可以用来定位的方法有:

    Browser.tr(:class,  “tr class”)

    Browser.tr(:id,  “tr id”)

    Browser.tr(:index,  0)

    Browser.tr(:name,  “tr name”)

    Browser.tr(:text,  “tr text”)

    Browser.tr(:xpath,  “tr xpath”)

    Browser.tr(:class=>“tr class name”,  :index, 0) (Mulitple Attributes)

     

    Select_list元素

    HTML tag - <select>

     

    可以用来定位的方法有:

    Browser.select(:class,  “select class”)

    Browser.select(:id,  “select id”)

    Browser.select(:index,  0)

    Browser.select(:name,  “select name”)

    Browser.select(:text,  “select text”)

    Browser.select(:value, “select value”)

    Browser.select(:xpath,  “select xpath”)

    Browser.select(:class=>“select class name”,  :index, 0) (Mulitple Attributes)

    Span元素

    HTML tag - <span>

     

    可以用来定位的方法有:

    Browser.span(:class,  “span class”)

    Browser.span(:id,  “span id”)

    Browser.span(:index,  0)

    Browser.span(:name,  “span name”)

    Browser.span(:text,  “span text”)

    Browser.span(:xpath,  “span xpath”)

    Browser.span(:class=>“span class name”,  :index, 0) (Mulitple Attributes)

  • 如何在 Watir 中识别各种 HTML 对象(5)

    2013-01-14 17:51:04

    Link元素

    HTML tag - <link>

     

    可以用来定位的方法有:

    Browser.link(:after?, “after link”)

    Browser.link(:class,  “link class”)

    Brower.link(:href, “link href”

    Browser.link(:id,  “link id”)

    Browser.link(:index,  0)

    Browser.link(:name,  “link name”)

    Browser.link(:text,  “link text”)

    Browser.link(:xpath,  “link xpath”)

    Browser.link(:class=>“link class name”,  :index, 0) (Mulitple Attributes)

     

    Map元素

    HTML tag - <map>

     

    可以用来定位的方法有:

    Browser.map(:class,  “map class”)

    Browser.map(:id,  “map id”)

    Browser.map(:index,  0)

    Browser.map(:name,  “map name”)

    Browser.map(:text,  “map text”)

    Browser.map(:xpath,  “map xpath”)

    Browser.map(:class=>“map class name”,  :index, 0) (Mulitple Attributes)

     

    P元素

    HTML tag - <p>

     

    可以用来定位的方法有:

    Browser.paragraph(:class,  “paragraph class”)

    Browser.paragraph(:id,  “paragraph id”)

    Browser.paragraph(:index,  0)

    Browser.paragraph(:name,  “paragraph name”)

    Browser.paragraph(:text,  “paragraph text”)

    Browser.paragraph(:xpath,  “paragraph xpath”)

    Browser.paragraph(:class=>“paragraph class name”,  :index, 0) (Mulitple Attributes)

     

    Pre元素

    HTML tag - <pre>

     

    可以用来定位的方法有:

    Browser.pre(:class,  “pre class”)

    Browser.pre(:id,  “pre id”)

    Browser.pre(:index,  0)

    Browser.pre(:name,  “pre name”)

    Browser.pre(:text,  “pre text”)

    Browser.pre(:xpath,  “pre xpath”)

    Browser.pre(:class=>“pre class name”,  :index, 0) (Mulitple Attributes)

  • 如何在 Watir 中识别各种 HTML 对象(4)

    2013-01-14 17:49:50

    Head元素

    HTML tag - <h1><h2><h3><h4><h5><h6>

     

    可以用来定位的方法有:

    Browser.head(:class,  “heading class”)

    Browser.head(:id,  “head id”)

    Browser.head(:index,  0)

    Browser.head(:name,  “head name”)

    Browser.head(:text,  “head text”)

    Browser.head(:xpath,  “head xpath”)

    Browser.head(:class=>“head class name”,  :index, 0) (Mulitple Attributes)

     

    Hidden元素

    HTML tag - <hidden>

     

    可以用来定位的方法有:

    Browser.hidden(:class,  “hidden class”)

    Browser.hidden(:id,  “hidden id”)

    Browser.hidden(:index,  0)

    Browser.hidden(:method, “hidden method”)

    Browser.hidden(:name,  “hidden name”)

    Browser.hidden(:text,  “hidden text”)

    Browser.hidden(:value, “hidden value”)

    Browser.hidden(:xpath,  “hidden xpath”)

    Browser.hidden(:class=>“hidden class name”,  :index, 0)(Mulitple Attributes)

     

    Image元素

    HTML tag - <img>

     

    可以用来定位的方法有:

    Browser.image(:alt, “image alt”)

    Browser.image(:class,  “image class”)

    Browser.image(:for,”image for”)

    Browser.image(:id,  “image id”)

    Browser.image(:index,  0)

    Browser.image(:name,  “image name”)

    Browser.image(:src,”image src”)

    Browser.image(:text,  “image text”)

    Browser.hidden(:value, “hidden value”)

    Browser.hidden(:xpath,  “hidden xpath”)

    Browser.hidden(:class=>“hidden class name”,  :index, 0)(Mulitple Attributes)

    Label元素

    HTML tag - <label>

     

    可以用来定位的方法有:

    Browser.label(:class,  “label class”)

    Browser.label(:id,  “label id”)

    Browser.label(:index,  0)

    Browser.label(:name,  “label name”)

    Browser.label(:text,  “label text”)

    Browser.label(:xpath,  “label xpath”)

    Browser.label(:class=>“label class name”,  :index, 0) (Mulitple Attributes)

     

    Li元素

    HTML tag - <li>

     

    可以用来定位的方法有:

    Browser.li(:class,  “li class”)

    Browser.li(:id,  “li id”)

    Browser.li(:index,  0)

    Browser.li(:name,  “li name”)

    Browser.li(:text,  “li text”)

    Browser.li(:xpath,  “li xpath”)

    Browser.li(:class=>“li class name”,  :index, 0) (Mulitple Attributes)

     


  • 如何在 Watir 中识别各种 HTML 对象(3)

    2013-01-14 17:48:45

    Div元素

    HTML tag - <div>

     

    可以用来定位的方法有:

    Browser.div(:class,  “div class”)

    Browser.div(:id,  “div id”)

    Browser.div(:index,  0)

    Browser.div(:name,  “div name”)

    Browser.div(:text,  “div text”)

    Browser.checkbox(:xpath,  “div xpath”)

    Browser.checkbox(:class=>“div class name”,  :index, 0) (Mulitple Attributes)

     

    File_field元素

    HTML tag - <input type=”file”>

     

    可以用来定位的方法有:

    Browser.filefield(:class,  “file class”)

    Browser.filefield(:id,  “file id”)

    Browser.filefield(:index,  0)

    Browser.filefield(:name,  “file name”)

    Browser.filefield(:title,  “file title”)

    Browser.filefield(:value,  “file value”)

    Browser.filefield(:xpath,  “file xpath”)

    Browser.filefield(:class=>“file class name”,  :index, 0) (Mulitple Attributes)

     

    Form元素

    HTML tag - <form>

     

    可以用来定位的方法有:

    Browser.form(:action, “form. action”)

    Browser.form(:class,  “file class”)

    Browser.form(:id,  “form. id”)

    Browser.form(:index,  0)

    Browser.form(:method,  “form. method”)

    Browser.form(:name,  “form. name”)

    Browser.form(:xpath,  “form. xpath”)

    Browser.form(:class=>“form. class name”,  :index, 0) (Mulitple Attributes)

     

    Frame元素

    HTML tag - <frame>/<iframe>

     

    可以用来定位的方法有:

    Browser.frame(:id,  “frame. id”)/

    Browser.frame(:index,  0)

    Browser.frame(:name,  “frame. name”)

    Browser.frame(:src,  “frame. src”)

    Browser.frame(:text,  “frame. text”)

    Browser.frame(:id=>“frame. id”,  :index, 0) (Mulitple Attributes)

     


  • 如何在 Watir 中识别各种 HTML 对象(2)

    2013-01-14 17:47:48

    Area元素

    HTML tag - <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(:xpath, “area xpath”)

    Browser.area(:class=>“area class name”, :id=>“area id”, :index, 0) (Mulitple Attributes)

     

    Button元素

    HTML tag - <button>/<input type=”button”>/<input type=”image”>/<input type=”reset”>

    /<input type=”submit”>

     

    可以用来定位的方法有:

    Browser.button(:alt,  “alt button name”)

    Browser.button(:class,  “button class”)

    Browser.button(:id,  “button id”)

    Browser.button(:index,  0)

    Browser.button(:name,  “button name”)

    Browser.button(:src,  “image button src”) (only for type is image)

    Browser.button(:text,  “button text”)

    Browser.button(:value,  “button default value”)

    Browser.button(:xpath,  “button xpath”)

    Browser.button(:class=>“button class name”, :id=>“button id”, :index, 0) (Mulitple Attributes)

     

    Cell元素

    HTML tag - <td>

     

    可以用来定位的方法有:

    Browser.td(:alt,  “alt button name”)

    Browser.td(:class,  “td class”)

    Browser.td(:id,  “td id”)

    Browser.td(:index,  0)

    Browser.td(:name,  “td name”)

    Browser.td(:text,  “td text”)

    Browser.button(:xpath,  “td xpath”)

    Browser.button(:class=>“td class name”, :id=>“td id”, :index, 0) (Mulitple Attributes)

     

     

    Checkbox元素

    HTML tag - <input type=”checkbox”>

     

    可以用来定位的方法有:

    Browser.checkbox(:class,  “checkbox class”)

    Browser.checkbox(:id,  “checkbox id”)

    Browser.checkbox(:index,  0)

    Browser.checkbox(:name,  “checkbox name”)

    Browser.checkbox(:text,  “checkbox text”)

    Browser.checkbox(:value,  “checkbox value”)

    Browser.checkbox(:xpath,  “checkbox xpath”)

    Browser.checkbox(:class=>“checkbox class name”,  :index, 0) (Mulitple Attributes)

  • 如何在 Watir 中识别各种 HTML 对象(1)

    2013-01-14 17:43:29

    Watir Web-driver 支持的 HTML elements 如下所列:


  • Cucubmer 没有when

    2013-01-14 10:45:51

    呵呵,今天突然想试试看,如果没有when的cucubmer是不是还能跑的通。

    这是原始文件:

    # language: zh-CN
    功能:加法
      为了避免一些愚蠢的错误
      作为一个数学白痴
      我希望有人告诉我数字相加的结果

      场景: 两个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        当我按相加按钮
        那么我应该在屏幕上看到的结果是13

      场景: 三个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        而且我已经在计算器里输入1 
        当我按相加按钮
        那么我应该在屏幕上看到的结果是14

    相应的define step文件是:

    # encoding: utf-8
    begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
    require 'cucumber/formatter/unicode'
    $:.unshift(File.dirname(__FILE__) + '/../../lib') 
    require 'calculator'

    Before do
      @calc = Calculator.new
    end

    After do
    end

    Given /我已经在计算器里输入(\d+)/ do |n|
      @calc.push n.to_i
    end

    When /我按(.*)按钮/ do |op|
      if p == '相加'
        @result = @calc.send "add"
      end
    end

    Then /我应该在屏幕上看到的结果是(.*)/ do |result|
      @result.should == result.to_f
    end

    这是原始例子 可以正常跑通:

    2 scenarios (2 passed)
    9 steps (9 passed)
    0m0.016s



    这是没有when的文件:

    # language: zh-CN
    功能:加法
      为了避免一些愚蠢的错误
      作为一个数学白痴
      我希望有人告诉我数字相加的结果

      场景: 两个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        而且我按相加按钮
        那么我应该在屏幕上看到的结果是13

      场景: 三个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        而且我已经在计算器里输入1 
        而且我按相加按钮
        那么我应该在屏幕上看到的结果是14

    相应修改的step define文件是:

    # encoding: utf-8
    begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
    require 'cucumber/formatter/unicode'
    $:.unshift(File.dirname(__FILE__) + '/../../lib') 
    require 'calculator'

    Before do
      @calc = Calculator.new
    end

    After do
    end

    Given /我已经在计算器里输入(\d+)/ do |n|
      @calc.push n.to_i
    end

    Given /我按(.*)按钮/ do |op|
      if p == '相加'
        @result = @calc.send "add"
      end
    end

    Then /我应该在屏幕上看到的结果是(.*)/ do |result|
      @result.should == result.to_f
    end

    也可以正常跑通:

    2 scenarios (2 passed)
    9 steps (9 passed)
    0m0.016s


    其实为了更好的能够分隔Given和Then,还是加一个什么都不做的When比较好:
    # language: zh-CN
    功能:加法
      为了避免一些愚蠢的错误
      作为一个数学白痴
      我希望有人告诉我数字相加的结果

      场景: 两个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        而且我按相加按钮
        当我开始验证结果
        那么我应该在屏幕上看到的结果是13

      场景: 三个数相加
        假如我已经在计算器里输入6
        而且我已经在计算器里输入7
        而且我已经在计算器里输入1 
        而且我按相加按钮
        当我开始验证结果
        那么我应该在屏幕上看到的结果是14

    相应的step define 文件是:

    # encoding: utf-8
    begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
    require 'cucumber/formatter/unicode'
    $:.unshift(File.dirname(__FILE__) + '/../../lib') 
    require 'calculator'

    Before do
      @calc = Calculator.new
    end

    After do
    end

    Given /我已经在计算器里输入(\d+)/ do |n|
      @calc.push n.to_i
    end

    Given /我按(.*)按钮/ do |op|
      if p == '相加'
        @result = @calc.send "add"
      end
    end

    When /我开始验证结果/ do ||
      puts '我开始验证结果'
    end

    Then /我应该在屏幕上看到的结果是(.*)/ do |result|
      @result.should == result.to_f
    end



    正常跑通:

    2 scenarios (2 passed)
    11 steps (11 passed)
    0m0.016s

    说明,cucumber是完全按照关键词+行去进行正则表达式的匹配的,各个步骤之间并没有逻辑关系,只是通过你的组织,实现了产品本身的业务逻辑。
  • Watir在cucumber上的应用

    2013-01-10 15:12:15

    接着昨天的table搞,那个table从上倒下一个value,id都没有,而且是动态的,偏偏还要像这样

    地图楼层修改 删除

    选择匹配行的链接进行操作。

    后来研究了半天发现element有个parent的方法,所以,简单了

    先匹配“地图楼层”这个td,然后返回该td的parent,即该行的tr,然后再index到该tr的第二个td,就可进行操作了:

    $browser.td(:text, '3333').parent.[](1).link(:text, '修改').click

    看来什么东西想精通,就得多用啊。

    然后根据cucumber的架构,搞了一些测试用例:

    在feature 文件中是这样的:

    # language: zh-CN
    功能:楼层管理
      作为一个管理人员
      我能够进行楼层管理

      场景: Case 1 - 验证界面元素(S2-ST-JBXXGL-LCGL-0001)
        假如进入基本信息管理界面
        当转入楼层管理界面
        那么我可以看到楼层编号
        并且我可以看到楼层名称
        并且我可以看到修改
        并且我可以看到删除
        并且我可以看到添加楼层
      
      场景: Case 2 - 正常添加楼层(S2-ST-JBXXGL-LCGL-0002)
        假如进入基本信息管理界面  
        而且我进入楼层管理界面    
        而且点击添加楼层          
        #而且我已经在楼层编号里输入001
        而且我已经在楼层名称里输入地下一层
        当点击添加楼层  
        那么我应该在楼层管理界面 
        #并且我可以看到001 
        并且我可以看到地下一层 

    然后再初期的define step中有这样的两行:

    Given /进入(.*)界面/ do |module_name|
      $autotest.navigate_to_module(module_name)
    end 

    When /进入(.*)界面/ do |module_name|
      $autotest.navigate_to_module(module_name)
    end 

    然后很和谐的就报错了,cucumber在匹配

    假如进入基本信息管理界面
    当进入楼层管理界面

    这两行的时候,神经错乱了,只好改成:

    假如进入基本信息管理界面
    当转入楼层管理界面


    Given /进入(.*)界面/ do |module_name|
      $autotest.navigate_to_module(module_name)
    end 

    When /转入(.*)界面/ do |module_name|
      $autotest.navigate_to_module(module_name)
    end 


    看来用中文写测试用例,也够费脑子的。

    经过一个模块的实验,发现435个手工测试用例,大概要定义45个step,然后需要定义56个functional

    不过,发现剩下的2000多个手工测试用例,需要新加的step应该不会超过20个 定义的功能也不会超过100个,就完成了所有的手工测试测试用例----》自动化测试用的转变

    哈哈 还是很厉害的,执行了20条case,大概一条在45秒, 就算一分钟, 2500个测试用里 差不多5个小时也能执行一遍,而手工测试用例,大概需要500个人/时


    所以,使用cucumber进行自动化测试,首先需要高质量的测试用例,然后还要进行挑选,没必要所有的测试用例都进行自动化。
  • Watir的应用

    2013-01-09 14:41:26

    SDD解决后,问题还是集中在Watir-webdriver的上应用。

    现在每天记录一点在开发过程中碰到的问题,以及解决的方法

    1. 支持中文

    因为要测试的网站是中文的,一开始搞了好久老是出错,后来偶然发现在Cucumber的step define 文件中,抬头的第一行就是: #encoding:utf-8。 copy过来,果然好使,哈哈。

    2. 企图添加assert

    发现老是报错,后来回头老老实实的看了一遍文档发现是这样的:

    首先 需要 require 'test/unit' 
    其次 需要 class TC_myTest < Test::Unit::TestCase
    然后 需要 def mytestcase
    最后 把你的测试代码置入,就可以加assert了

    3. 操作页面上一个table

    常规的操作很简单:
    1. $browser.table(:id=>"t1").rows #所有的行  
    2. $browser.table(:id=>"t1").[](0) #获取第一行  
    3. $browser.table(:id=>"t1").[](0).cells #获取第一行的所有单元格  
    4. $browser.table(:id=>"t1").[](0).[](0) #获取第一行第一列的单元格  

    偏偏碰上了个巨懒的程序员,代码中就一行: <table width="90%">

    研究了半天,只要用最笨的方法:
    $browser.table(:index, 7).rows

    一个个数了半天,回头要求开发加上
  • SDD开发实例(6)

    2012-12-07 16:32:57

    经过一周的练习,发现最大的问题就是QA开发的test case质量不高,很多人把测试用例写的跟流程文档或者需求文档很类似。

    其实这个问题由来已久,很多QA都觉得写QA很简单,翻来覆去就是那几种正交,等价类之类的,其实产品的质量基于测试的质量,而测试的质量依赖于test case的质量。

    在SDD中,我们是基于Story去开发测试用例,所以我们要明确出来的是我们系统中存在哪些对象,这些对象有哪些方法可以被调用,一个data-driven的书写标准,不单能让我们获得最小的step集合,从而能够尽量的代码复用,而且能够很清楚的表明这些对象所自有的方法(inside)和互相传递数据的方法(outside),从而获得足够高的cover率和尽可能低的duplicate coverage。

    我们以下面为例子:

    在界面中,有很多流程都是在一个手机号text field中输入不同的数据,所以Qa们都是根据流程去书写这些测试用例:

    2. 不输入手机号

    13. 输入手机号

    2. 输入超过20个字符的手机号

    2. 输入特殊字符的手机号信息

     

    实际上,所有的这些测试步骤都可以用一个步骤代替

     

    输入手机号XXX

     

    我们只需要在test description分别说明,就能让其他测试人员明白你要干什么


    这样 test description 就分别为:

     

    手机号码为空

    输入正常手机号

    输入超过11个数字的手机号

    输入特殊字符的手机号


    而依据data-driven方式书写的测试用例步骤为: 

    (XXX为该条语句的输入值)


    输入手机号(空)

    输入手机号13800138000

    输入手机号1234567890123

    输入手机号123AAA

    从上面我们可以看出,良好的书写格式和习惯,实际上使你的测试用例不但更加健壮,而且易于进行automation。

    很多项目的automation进展困难,就是因为依据这些手工测试脚本创建自动化脚本过于复杂化,重新书写测试用例又代价太高,就在这进退为难的过程中,从而失败的推广自动化测试。 

  • SDD开发实例(5)

    2012-12-07 16:30:27

    很多人可能对正则表达式不太清楚,而我们要在Define steps中使用这些正则表达式 所以 给出这些常用的:

    正则表达式全集

    字符

    描述

    \

    将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,n匹配字符n\n匹配一个换行符。串行\\匹配\\(则匹配(

    ^

    匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配\n\r之后的位置。

    $

    匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配\n\r之前的位置。

    *

    匹配前面的子表达式零次或多次。例如,zo*能匹配z以及zoo*等价于{0,}

    +

    匹配前面的子表达式一次或多次。例如,zo+能匹配zo以及zoo,但不能匹配z+等价于{1,}

    ?

    匹配前面的子表达式零次或一次。例如,do(es)?可以匹配doesdoes中的do?等价于{0,1}

    {n}

    n是一个非负整数。匹配确定的n次。例如,o{2}不能匹配Bob中的o,但是能匹配food中的两个o

    {n,}

    n是一个非负整数。至少匹配n次。例如,o{2,}不能匹配Bob中的o,但能匹配foooood中的所有oo{1,}等价于o+o{0,}则等价于o*

    {n,m}

    mn均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,o{1,3}将匹配fooooood中的前三个oo{0,1}等价于o?。请注意在逗号和两个数之间不能有空格。

    ?

    当该字符紧跟在任何一个其他限制符(*,+,?{n}{n,}{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串ooooo+?将匹配单个o,而o+将匹配所有o

    .

    匹配除\n之外的任何单个字符。要匹配包括\n在内的任何字符,请使用像(.|\n)的模式。

    (pattern)

    匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用\(\)

    (?:pattern)

    匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符(|)来组合一个模式的各个部分是很有用。例如industr(?:y|ies)就是一个比industry|industries更简略的表达式。

    (?=pattern)

    正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,Windows(?=95|98|NT|2000)能匹配Windows2000中的Windows,但不能匹配Windows3.1中的Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

    (?!pattern)

    正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如Windows(?!95|98|NT|2000)能匹配Windows3.1中的Windows,但不能匹配Windows2000中的Windows。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始

    (?<=pattern)

    反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,(?<=95|98|NT|2000)Windows能匹配2000Windows中的Windows,但不能匹配3.1Windows中的Windows

    (?<!pattern)

    反向否定预查,与正向否定预查类拟,只是方向相反。例如(?<!95|98|NT|2000)Windows能匹配3.1Windows中的Windows,但不能匹配2000Windows中的Windows

    x|y

    匹配xy。例如,z|food能匹配zfood(z|f)ood则匹配zoodfood

    [xyz]

    字符集合。匹配所包含的任意一个字符。例如,[abc]可以匹配plain中的a

    [^xyz]

    负值字符集合。匹配未包含的任意字符。例如,[^abc]可以匹配plain中的p

    [a-z]

    字符范围。匹配指定范围内的任意字符。例如,[a-z]可以匹配az范围内的任意小写字母字符。

    [^a-z]

    负值字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z]可以匹配任何不在az范围内的任意字符。

    \b

    匹配一个单词边界,也就是指单词和空格间的位置。例如,er\b可以匹配never中的er,但不能匹配verb中的er

    \B

    匹配非单词边界。er\B能匹配verb中的er,但不能匹配never中的er

    \cx

    匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Za-z之一。否则,将c视为一个原义的c字符。

    \d

    匹配一个数字字符。等价于[0-9]

    \D

    匹配一个非数字字符。等价于[^0-9]

    \f

    匹配一个换页符。等价于\x0c\cL

    \n

    匹配一个换行符。等价于\x0a\cJ

    \r

    匹配一个回车符。等价于\x0d\cM

    查看(614) 评论(0) 收藏 分享 管理

  • SDD开发实例(4)

    2012-12-04 17:26:55

    首先,我们看看从客户方面得到的测试用例


    case 1: 第一次正常开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入该卡号对应的”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 返回”定位卡管理“界面,刚输入的卡号可以在该界面查询

    case 2: 不输入任何信息”开卡“

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 不输入任何信息
    3. 点击开卡
    4. 输入正确的”卡号“
    5. 不输入”激活码“
    6. 点击”开卡“
    7. 输入正确的卡号、激活码、不输入”初始金额“
    8. 点击开卡
    "
    结果:
    "3.  输入框为”红框“提醒→”请输入卡号“,如图
    6. 输入框为”红框“提醒→”请输入激活码“
    8.1  界面弹出提示”开卡成功“
    8.2 界面返回”定位卡管理“信息页面
    8.3 初始金额默认为0"


    case 3: 输入已存在的卡号开卡

    已存在定位卡信息

    "1. 点击”开卡“
    2. 输入已存在的”卡号“
    3. 输入该卡号对应的”激活码“
    4. 点击”开卡“"

    结果:
    4. 提示→”该卡号已存在“


    case 4: 输入不符合格式标准的卡号开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入错误格式的”卡号“
    3. 输入任意”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”卡号有误“


    case 5: 超长字符的卡号开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入超长的”卡号“
    3. 输入正确的”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”卡号有误“


    case 6: 特殊字符卡号开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入特殊字符的”卡号“
    3. 输入正确的”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”卡号有误“


    case 7: 取消按钮验证

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入正确的”激活码“
    4. 输入”初始金额300“
    5. 点击”取消“"

    结果:
    "5.1  界面返回”定位卡管理“信息页面
    5.2  不增加新的定位卡信息"


    case 8: 输入正确的卡号,错误的激活码开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入错误格式”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”激活码有误“(待议)


    case 9: 输入超长激活码开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入超长的”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”激活码有误“(待议)


    case 10: 特殊字符激活码开卡

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入特殊字符”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→”激活码有误“(待议)

    case 11: 卡号正确定性验证

    进入”定位卡管理“界面

    "1. 点击”开卡“
    2. 输入正确的”卡号“
    3. 输入不匹配的”激活码“
    4. 输入”初始金额300“
    5. 点击”开卡“"

    结果:
    5. 提示→“卡号和激活码不匹配”(待议)


    依照以上的测试用例 书写feature文件:

    # language: zh-CN
    功能:开卡
      作为一个管理人员
      我能够给新用户激活一张新定位卡

      场景: Case 1 - 正常开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且刚输入的卡号可以在该界面查询

      场景: Case 2 - 不输入任何信息“开卡”
        假如点击开卡按键
        #而且我已经在卡号框里输入
        #而且我已经在激活码框里输入
        #而且我已经在初始金额框里输入
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒请输入卡号
        并且提醒请输入激活码
      
      场景: Case 3 - 输入已存在的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒该卡号已存在
       
      场景: Case 4 - 输入不符合格式标准的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入错误卡号1-2-3
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒卡号有误

      场景: Case 5 - 输入超长字符的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入错误卡号aaa
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒卡号有误

      场景: Case 6 - 输入特殊字符的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入错误卡号@#¥
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒卡号有误
      
      场景: Case 7 - 取消开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击取消按键
        那么我应该在定位卡管理界面
        并且不增加新的定位卡信息

      场景: Case 8 - 输入正确的卡号,错误的激活码开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入错误激活码aaa
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒激活码有误

      场景: Case 9 - 输入超长激活码开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入错误激活码aaaaaaa
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒激活码有误

      场景: Case 10 - 特殊字符激活码开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入错误激活码aaaa@#$
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒激活码有误

      场景: Case 11 - 卡号正确定性验证
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入错误激活码123
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒激活码有误

    而与之关联的define_step 文件为:

    # encoding: utf-8
    begin require 'rspec/expectations'; rescue LoadError; require 'spec/expectations'; end
    require 'cucumber/formatter/unicode'
    require "watir-webdriver"
    $:.unshift(File.dirname(__FILE__) + '/../../lib') 


    Before do
      #$page=Watir::Browser.new:ff
      #Login_action
      #Navigate_to_manage_card_action
    end

    After do
    #Logout_action
    end

    Given /^点击(.*)按键$/ do |button|
      if button =="开卡"
      #$page.button(:name, "开卡").click
      end
     
    end


    Given /^我已经在卡号框里输入(\d+)$/ do |card_number|
    #$page.text_field(:name,"card_number").set(card_number)
    end

    Given /^我已经在卡号框里输入错误卡号(.*)$/ do |card_number|
    #$page.text_field(:name,"card_number").set(card_number)
    end

    Given /^我已经在激活码框里输入(\d+)$/ do |act_number|
    #$page.text_field(:name,"act_number").set(act_number)
    end

    Given /^我已经在激活码框里输入错误激活码(.*)$/ do |act_number|
    #$page.text_field(:name,"act_number").set(act_number)
    end

    Given /^我已经在初始金额框里输入(\d+)$/ do |ini_money|
    #$page.text_field(:name,"money").set(ini_money)
    end


    When /^我点击(.*)按键$/ do |button_name|
      if button_name =="开卡"
      #$page.button(:name, "开卡").click
      elsif button_name == "消卡"
      #do cancel
      end
    end

    Then /^我应该在定位卡管理界面$/ do 
        #page_on_manage_card_page
    end

    Then /^刚输入的卡号可以在该界面查询$/ do 
      #card_number_can_search_out
    end

    Then /^不增加新的定位卡信息$/ do
    # log
    end

    Then /^提醒(.*)$/ do |message| 
      if message =="请输入卡号" 
      # log 
      end
      
      if message =="请输入激活码" 
      # log
      end

      if message == "该卡号已存在"
      #log
      end

      if message =="卡号有误"
      #log
      end

      if message =="激活码有误"
      #log
      end
    end


    现在我们对这些进行分析:

    首先:我们需要在before do里面做一系列的操作
    1. login
    2. 进入”定位卡管理“界面

    在 after do 里面 可考虑的是
    1. logout

    然后 我们看这些具体的实例

    很显然 case 1 的结果判断过于复杂 是通过一系列的查询和匹配工作来确定卡是否被正确的开通。

    这个代码,我们完全可以跟查询模块的代码进行复用。只学要按照刚输入的卡号去查询,能够显示在查询列表中,表示该卡已经激活。(隐含,也许是需要获得该卡的状态)

    另外 有些case是判断输入的开号是否超长 实际上 我们不需要去判断一个超长的卡号是否返回正确的信息,我们只需要判断一下该文本域的length即可

    另外 对于错误的输入,错误的格式输入,特殊字符的输入 都是同一个类型的检验 
    所以我们可以使用tag 然后在回归测试中 只需要执行一条即可

    对于激活码和卡号不匹配 其实是等同于错误的卡号/错误的激活码

    最后 就是所有的结果判断都不是明确的判断 容易引起歧义



    所以 通过这个过程我们可以将case 重新组织为:

    # language: zh-CN
    功能:开卡
      作为一个管理人员
      我能够给新用户激活一张新定位卡

      场景: Case 1 - 正常开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且刚输入的卡号可以在该界面查询

      场景: Case 2 - 不输入任何信息“开卡”
        假如点击开卡按键
        #而且我已经在卡号框里输入
        #而且我已经在激活码框里输入
        #而且我已经在初始金额框里输入
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒请输入卡号
        并且提醒请输入激活码
      
      场景: Case 3 - 输入已存在的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒该卡号已存在
       
      场景: Case 4 - 输入不符合格式标准的卡号开卡
        假如点击开卡按键
        而且我已经在卡号框里输入错误卡号1-2-3
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒卡号有误




      
      场景: Case 7 - 取消开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入999
        而且我已经在初始金额框里输入300
        当我点击取消按键
        那么我应该在定位卡管理界面
        并且不增加新的定位卡信息

      场景: Case 8 - 输入正确的卡号,错误的激活码开卡
        假如点击开卡按键
        而且我已经在卡号框里输入123
        而且我已经在激活码框里输入错误激活码aaa
        而且我已经在初始金额框里输入300
        当我点击开卡按键
        那么我应该在定位卡管理界面
        并且提醒激活码有误
  • SDD开发实例(3)

    2012-12-04 15:14:43

    cucumber是一个SDD框架,使用Gherkin语言。 

    当你编写一个feature文件的时候,会用到很多Gherkin语言里特定的关键字,主要包括下这面些:  
    • Feature   -  功能  
    • Background  - 背景  
    • Scenario  -  场景  
    • Scenario outline  - 场景  
    • Scenarios (or examples) - 实例  
    • Given - 假如  
    • When - 当  
    • Then - 那么  
    • And (or but) -并且/但是  
    • | (用来定义表格)  
    • """ (定义多行字符串)  
    • # (注释) 


    每一个feature文件必须以关键字Feature开始,且紧跟着一个冒号和一个描述。

    一个feature文件里面可以有很多个Scenario,一个Scenario就是一个具体的你想要测试的功能点

    关键字Given, When, Then, And 和 But用来指示一个Scenario中的步骤。

    在define steps中 我们可以使用正则表达式

    Given /我已经在计算器里输入(\d+)/ do |n| 
     @calc.push n.to_i 
    end

    Then /我应该在屏幕上看到的结果是(.*)/ do |result| 
     @result.should == result.to_f 
    end

    (\d+) 用于匹配数字 例如上例 将输入后面的数字赋值给n
    (.*) 用于匹配字符  例如上例 将结果是后面的字符串赋值给result

  • SDD开发实例(2)

    2012-12-04 14:20:39

    敏捷开发,基本上属于现在软件开发的主潮流。但是很多人还没有真正的理解它。敏捷开发并不是指特定某种开发模式,而是概指所有符合以下特性的任意开发模式:通过更加频繁有效的交流,更早的发现问题,解决问题。

     

    基本上主流的敏捷方法,都是依赖于测试的质量,所以测试驱动开发(Test Driven Development)可以称为敏捷开发的基柱。TDD的思想就是通过测试活动来驱动开发活动从而达到将客户需求能够精确的反应到开发过程中,从而降低开发的成本。在这过程中,最典型的特点就是先完成测试程序,在实现能够通过测试程序验收的代码,经过反复迭代,从而使得产品顺利发布。

     

    TDD的进化过程中,出现了一个更加有趣的BDD分支,即行为驱动开发(Behavior. Driven Development)。在不断的迭代过程和OO思想影响下,我们会发现有很多产品的设计思路从总体来看就是一些特定对象的行为集合。我们的产品,就是不断的完善这些对象的行为。因此,TDD的真正价值就在于能够更加清晰有效的对对象的行为进行分解实现。对于拥有BA的开发团队,一般将用户的需求描述为产品的设计理念,例如UML的介入,大量的流程图和关系图的出现,将简单的需求复杂化,模块化。那么为什么不直接从真正用户如何使用产品入手呢?这样我们直接就从客户的角度考虑,如何设计产品,从而使其的行为能够符合用户的习惯(UE)和流程。BDD就是依据这一理念出现了。

     

    BDD通过定义用户和开发团队所共同接受的“通用语言”来实现对接。从而避免双方表达不一致的出现,导致产品开发出现产品功能偏差。

     

    SDDStory Driven Development)就是对BDD的一种具体的实现。使用Story来定义用户和系统行为,从而达到测试驱动开发的目的。

     

    一般常用的story格式如下:

    Storysubject                           

    As a role

    In order to behavior

    So that benefit

     

    然后通过一系列的场景来验证该story的实现。

    如果我们把AC 看作test case outline,这些scenario 就等同于一个个我们要使用的test case

    一个scenario一般是如下格式:

    Scenario 1 subject

    Given 【上下文】

              And 【更多的上下文或步骤】

    When 【要完成的事件】

    Then 【期望的结果】

              And 【更多的期望结果】

     

    场景很容易去理解,Given And 给出了一些的上下文,就等同于测试用例中的preconditionsteps When给出的就是最后要验证的系统的behavior或者要验证的操作。ThenAnd给出的是这个behavior或者操作的正确期望结果。

     

    Story As a In order to So thatScenarioGivenAndWhenThen这些就是我们在SDD这种模式中定义的用户和开发团队所共同接受的“通用语言”的一些关键词。SDD框架能够理解和解释这些关键词从而使得SDD得到实现。

      

    Cucumber就是一个实现的SDD框架。它通过feature文件来保存story和相关的scenario。使用任意支持的语言来完成scenario中的steps与自动化测试脚本的mapping。然后就可以在执行feature文件时,直接执行相关的自动化测试脚本。

  • SDD开发实例(1)

    2012-12-04 10:01:52

    现在越来越多的项目采用敏捷开发模式。不管是真的敏捷,还是假的敏捷,总之接触到的国内项目基本上都是挂着羊头卖狗肉。不过不管怎么着,很多人越来越熟悉user story。其实,作为一个测试人员,如果只会单纯的测试将来的路只会越来越窄,复合型人才才是最终的目标。

    所以,一个QA,如果想将来有所成就,而不是混吃等死,最起码应该具备BA,QA,Automation Development,Configuration Admin,Network topology 等方面的相关知识。

    本篇开始,我们就讨论一下一个QA怎么去充当一个BA的角色。那就是SDD开发模式。SDD,Story Driven Development,是以user story为驱动方式的开发模式,主要是:

    BA(QA)书写user story =》QA 实现测试用例   =》测试  =》发布
                          =》Dev 开始实现代码

    所以从上可知,主要是QA在Dev完成代码开发的同时完成了测试用例,从而并发节省时间。

    一般来说SDD都是以自动化测试为住。我们就拿一个ruby+cucubmer+watir的自动化框架来作为实例。


    怎么去安装这些工具很简单,我们就不多说了,自己google吧。

    首先我们要理解cucubmer实现SDD的方法:

    1. 书写user story 和每个story所可能出现的scenario
    2. 将scenario的每个step通过正则表达式来map到自动化测试脚本
    3. 实现自动化测试脚本


    按上面的步骤 我们现在举一个实例

    现在有一个俱乐部的会员系统,能实现的功能有:基本信息管理,积分管理,产品管理和机构管理
    在基本信息管理中,我们可以开卡,删卡和改卡

    那么我们就可以有如下user story

    user story 1:
    作为一个信息管理人员
    我可以开通一个新的会员卡

    user story 2:
    作为一个信息管理人员
    我可以修改一个指定的会员卡

    user story 3:
    作为一个信息管理人员
    我可以删除一个指定的会员卡


    我们拿user story 1来进行scenario的描述

    user story 1:
    作为一个信息管理人员
    我可以开通一个新的会员卡

    scenario 1:
    假如我已经输入了新的会员卡卡号123
    并且输入了卡号对应的激活码999
    并且输入了初始金额500
    当我点击开卡按键时
    系统报告新卡正确开通

    scenario 2:
    假如我输入了卡号对应的激活码999
    并且输入了初始金额500
    当我点击开卡按键时
    系统报错需要输入用户卡号

    scenario 3:
    假如我已经输入了新的会员卡卡号123
    并且输入了初始金额500
    当我点击开卡按键时
    系统报错需要输入激活码

    scenario 4:
    假如我已经输入了新的会员卡卡号123
    并且输入了卡号对应的激活码999
    当我点击开卡按键时
    系统报错需要输入初始金额

    scenario 5:
    假如我已经输入了新的会员卡卡号123
    并且输入了卡号对应的激活码000
    并且输入了初始金额500
    当我点击开卡按键时
    系统报错错误的激活码

    scenario 6:
    假如我已经输入了新的会员卡卡号1234
    并且输入了卡号对应的激活码000
    并且输入了初始金额500
    当我点击开卡按键时
    系统报错卡号已经使用

    我们可以研究一下上面的scenario,

    simple的步骤,或者说唯一的步骤有
    我已经输入了新的会员卡卡号#d   (#d 代表卡号)
    我输入了卡号对应的激活码#d      (#d 代表激活码)
    我输入了初始金额#d             (#d 代表金额)
    我点击开卡按键     
    系统报告新卡正确开通
    系统报错错误的激活码/需要输入初始金额/需要输入激活码/卡号已经使用/需要输入用户卡号

    也就说 我只需要把以上的步骤进行整合,就能用如下语句描述

    scenario 1:
    假如我输入了123在会员卡卡号
    并且我输入了999在激活码
    并且我输入了500在初始金额
    当我点击开卡按键
    系统显示信息新卡正确开通

    scenario 2:
    假如我输入了999在激活码
    并且我输入了500在初始金额
    当我点击开卡按键
    系统显示信息需要输入用户卡号

    scenario 3:
    假如我输入了123在会员卡卡号
    并且我输入了500在初始金额
    当我点击开卡按键
    系统显示信息需要输入激活码

    scenario 4:
    假如我输入了123在会员卡卡号
    并且我输入了999在激活码
    当我点击开卡按键
    系统显示信息需要初始金额

    scenario 5:
    假如我输入了123在会员卡卡号
    并且我输入了000在激活码
    并且我输入了500在初始金额
    当我点击开卡按键
    系统显示信息错误的激活码

    scenario 6:
    假如我输入了1234在会员卡卡号
    并且我输入了000在激活码
    并且我输入了500在初始金额
    当我点击开卡按键
    系统显示信息已使用的会员卡


    这样,这些就可以存储在一个properties文件中

    然后 我们define这些step
    Given /我输入了#d在会员卡卡号/ then do
    customer_number.entry(#d)
    end

    Given /我输入了#d在激活码/ then do
    customer_actcode.entry(#d)
    end

    Given /并且我输入了#d在初始金额/ then do
    customer_inimoney.entry(#d)
    end

    Given /当我点击#s按键/ then do
    button(:value, #s).click
    end

    Given/ 系统显示信息#s/ then do
      message.verify(#s)
    end

    这样 我们就完成了每个自然语言描述的步骤对应的自动化测试脚本的步骤

    然后 我们就可以去实现这些自动化步骤,即使我们不知道最后的代码如何,但是依据我们的自动化测试框架,我们就可以开始构建这些自动化测试代码

    Module New_Customer_Card

    def customer_number.entry(no)
    $page.text_field(:value, customer number).set(no)
    end

    ......

    def  message.verify(message)
    if case 1 then message == "新卡正确开通"
    ...

    end

    当然 你也可以把你的代码构建的更加简练:

    在define 步骤中:

    Given /我输入了#d在#s/ then do
    customer.entry(#d,#s)
    end

    在自动化测试脚本:
    def customer.entry(number_value,field_label)
    $page.text_field(:vaule, field_label).set(number_value)
    end

    好了 现在大家大概能明白 如何使用cucumber去实现SDD了,我们接下来将会详细的进行分析和实现。
  • 测试用例能带来的(测试人生系列之六)

    2012-08-02 16:47:39

    通过测试用例,我们都能获得些什么呢?

    1. 测试团队的质量判断。例如,测试用例的覆盖率。我们只需要去把所有的valid的功能bug去做一个分析,用所有在测试用例覆盖范围之外的bug数/总bug数,就可以作为测试用例覆盖率使用。一个良好的测试团队,这个覆盖率应该在80%以上。
    2. 测试人员的质量判断。一个测试人员,最重要的是测试用例的质量,而不是发现的bug的多少。
    3. 提高开发代码的质量。选择高severity和priority的测试用例,给开发人员进行smoke testing。从而使得进入测试阶段的代码已经避免了大部分P1和P2的bug存在。提高了测试的效率,减少了测试的资源消耗。
    4. 合理的制定测试计划。在制定功能测试阶段的计划,需要的人力资源,可以通过测试用例的执行平均效率来估算。从而得出大致的测试跨度。
    5. 通过对测试文档的共同review,可以让BA能够确认他们设计和需求分析转换的效率。开发人员能够更详细细致的了解所要开发的模块。
    6. 通过测试用例的complete rate,failed rate等,管理层可以很好的跟踪项目的质量状况和进度。
    7. 通过测试用例的分析,可以避免overlap区域测试用例的缺失。
    8. 最终用户可以使用测试用例熟悉产品。
    9. Tech writer使用测试用例来书写用户手册是产品说明。
    10. 开发人员通过bug相关的测试用例能够更快的发现问题和更好的修复问题,减少带来的间接影响导致的新bug。
    11. 配置管理人员根据测试用例来更有效率的管理测试资源和环境。
    12. support人员可以根据测试用例来对客户进行更加有效的支持。
  • QA的核心价值(测试人生系列之五)

    2012-08-02 09:23:16

    任何事物都有一个核心,QA也有自己的独特的核心价值。那么这个核心价值是什么呢?

    我认为就是测试用例。

    测试用例作为QA的核心价值,起到了如下的作用:

    1.产品质量的评判依据。一个产品的质量到底如何,在release之前,我们需要一个风向标来判断是否可以进行release。对一个健康的项目来说,这个风向标一般都是所有的测试用例已经处于pass的状态。

    2.需求确认的载体。当BA人员对客户的需求进行分析,总结和形成文档后,如何保证需求能够被真实的实现?BA部门就是对测试用例进行审核,通过测试用例的分析,来确保需求实现的完整度。

    3. 开发的自我分析的依据。当测试用例完成后,开发阶段往往还在进行中。开发人员在完成单元测试和集成测试后,如果利用测试用例对自己的模块进行冒烟测试,那么即增加了开发提交给测试的代码的质量,又减少了项目的整体反转时间。从而能够把工作强度和风险管理压制在合理的成度。

    4. 测试计划的基石。对于产品的测试计划,首要的依据就是测试用例。需要根据测试用例来填写时间表,进度表,资源表,风险表等等。而且一个计划完成之后,需要进行不停动态更新,依据也是测试用例的状态分析结果。

    5. 管理的对象。对于项目测试进行管理,各种报告的形成,都是来源于测试用例的各种分析。

    6. 工作的内容。测试人员的主要工作内容就是进行测试用例的编写,审核,执行,分析。

    7. 培训的资料。测试用例是最简单,最直接,最有效的用户培训资料。产品的使用文档,用户说明等等都是tech writer基于测试用例进行升华的。

    8. 团队的桥梁。测试用例作为一个唯一的桥梁连接着最终用户,需求人员,设计人员,开发人员,管理人员,测试人员,过程控制人员,文档人员,资源管理人员,支持人员,销售人员等所有项目有关人员。

    所以,测试用例的质量基本上是产品质量的唯一决定因素。作为QA的核心价值,是我们主要目的。
  • 1828/10<12345678910>