• Seleniumwebdriver系列教程(8)————使用Page Object设计模式


    在进行web前端自动化测试的过程中,Page Object设计模式可以称得上是杀人放火,居家旅行的常备武器。

    Page Object将测试对象及单个的测试步骤封装在每个Page对象中,以page为单位进行管理。举例来说,在没有使用Page Object模式的情况下,脚本可能是这样写的:

    require 'rubygems'
    require 'selenium-webdriver'
     
    url = 'www.soso.com'
    dr = Selenium::WebDriver.for :ie
    dr.navigate.to url
     
    ua_links = dr.find_element(:id => 'ua').find_elements(:css => 'a')
    ua_links[3].click
    ua_links[3].send_keys(:enter)
    sleep 2
    dr.switch_to.frame('login_frame')
    user = {:name => 'test', :psd => 'test'}
    dr.find_element(:id => 'u').send_keys(user[:name])
    dr.find_element(:id => 'p').send_keys(user[:psd])
    dr.find_element(:id => 'login_button').click
    sleep 2
    dr.close
    

    这段脚本的作用是去到soso,然后点击【登陆】link,待登陆的dialog弹出后输入用户名和密码,点击【登陆】按钮进行登陆。

    看上去这段脚本是不错的,因为其很好的完成了登陆的任务,但不妨深入思考一下,如果我们需要验证错误的用户名正确的密码的话,那么上面关于登陆的那几行脚本是不是需要重复写一遍?

    答案是否定的,因为登陆的功能可以抽象成函数,如下所示:

    def login usr, psd
        dr.find_element(:id => 'u').send_keys(usr)
        dr.find_element(:id => 'p').send_keys(psd)
        dr.find_element(:id => 'login_button').click
    end
    

    这样就可以通过构造不同的数据,每次测试时只需要调用login函数就可以了。

    再深入想一下,假设需要测试不输入用户名和密码直接点击登陆按钮的情况,那该怎么办呢?

    首先最容易想到的一点是改造login函数,当然usr和psd没有传入的时候就直接点点击登陆按钮,这是没有问题的。不过如果本着代码增强可读性原则,亦可以定义一个名为login_without_usr_psd的函数,如下所示

    def login_without_usr_psd
        dr.find_element(:id => 'login_button').click
    end
    

    这样在编写测试用例代码的时候就可以直接凭借函数名来揣测出该函数的作用,起到了self explain的作用。在自动化测试代码中,这一点是被鼓励的。

    不过这样多定义1个函数就会带来另外的问题 dr.find_element(:id => 'login_button').click这行代码就在loin函数和login_without_usr_psd中重复使用。

    为了解决这个有些丑陋的问题,使得代码能够稍微美化一点,可以定义另外一个函数来实现点击登陆按钮的功能。

    def click_login_btn
        dr.find_element(:id => 'login_button').click
    end
     
    # now login_without_usr_psd will like below
    def login_without_usr_psd
        click_login_btn
    end
     
    # login function will like this
    def login usr, psd
        dr.find_element(:id => 'u').send_keys(usr)
        dr.find_element(:id => 'p').send_keys(psd)
        click_login_btn
    end
    

    好了,那么本着使代码更加灵活,self explain 特性更加明显的原则,我们又可以将输入用户名输入用户密码功能抽象成函数,这时候login函数看起来可能会是这样的:

    def login usr, psd
        set_usr usr
        set_psd psd
        click_login_btn
    end
    

    如果我们重复上面的步骤,一步一步的提升代码的复用性和可读性的话,我们就会发现除了上面演示的set_usr,set_psd这样的【基本动作】外,有些测试对象也是可以复用的。比如在登陆的时候我们会用到密码输入框,也许在修改密码的时候我们也会用到这一对象。

    很自然的就会想到,如果将一些测试对象以及操作这些测试对象的动作或步骤封装在1个类中,那么代码的灵活性和适用性将会更强。那么按照什么纬度来划分这些类呢?也是很自然的,就像睡醒了就会睁眼,挖完煤就要洗脸一样,我们会发现按照页面也就是page来组织这些类将是很好的解决方案。

    这就是Page Object设计模式,将每一个测试页面抽象为1个Page类,并在该类中封装了本页面的测试对象和基本的测试步骤,以提高代码的可读性复用性通用性和一致性。Page Object设计模式带来的好处是显而易见的。比如使用了page object模式后,测试用例可能会如下面所示

    soso_page = Site.new(dr).soso_main_page(url).open
    soso_page.login wrong_usr, wrong_psd
    soso_page.error_msg.should be_eql('error')
    

    测试用例简单且易读,而且代码的复用性极佳。其他用例需要使用到login功能时候只需要new 包含该功能的Page对象,调用login方法既可。

    下面的代码演示了如何使用Page Object设计模式重构本文开头所实现的soso主页用户登陆功能。

    base_page.rb
     
    class BrowserContainer
        def initialize driver
            @dr = driver
        end
    end # BrowserContainer
     
    class Site < BrowserContainer
        def soso_main_page url
            @soso_main_page = SosoMainPage.new(@dr, url)
        end
     
        def close
            @dr.close
        end
    end #Site
     
    class BasePage < BrowserContainer
        attr_reader :url
     
        def initialize dr, url
            super(dr)
            @url = url  
        end
     
        def open
            @dr.navigate.to @url    
            self
        end
    end #BasePage
     
    class SosoMainPage < BasePage
        require './login_dialog'
        include LoginDialog
     
        def login usr, psd
            open_login_dialog
            to_dialog_frame
            usr_field.send_keys usr 
            psd_field.send_keys psd
            login_btn.click
        end
     
        def open_login_dialog
            login_link.click
            login_link.send_keys(:enter)
            sleep 2
        end
     
        private
     
        def ua_links
            @dr.find_element(:id => 'ua').find_elements(:css => 'a')
        end
     
        def login_link
            ua_links[3]
        end 
    end #SosoMainPage
     
    login_dialog.rb     
     
    module LoginDialog
        def to_dialog_frame
            begin
                @dr.switch_to.frame('login_frame')  
            rescue
                raise 'Can not switch to login dialog, make sure the dialog was open'
                exit
            end
        end
     
        def usr_field
            @dr.find_element(:id => 'u')
        end
     
        def psd_field
            @dr.find_element(:id => 'p')
        end
     
        def login_btn
            @dr.find_element(:id => 'login_button')
        end
    end #LoginDialog
     
    login.rb
     
    require 'rubygems'
    require 'selenium-webdriver'
    require './base_page'
     
    dr = Selenium::WebDriver.for :firefox
    url = 'http://www.soso.com'
    soso_page = Site.new(dr).soso_main_page(url).open
    soso_page.login 'test', 'test'
    

    上面的代码由3个文件组成。

    base_page.rb文件中定义了

    • Site类 主要用于管理测试中所需要用到的各种页面,提供生成这些页面对象的快捷方法。比如Site.new(dr).soso_main_page(url)方法就实例化了1个SosoMainPage对象。

    • BasePage类 所有Page对象的基类

    • SosoMainPage类 代表了soso主页的Page Object类,封装了首页的一些测试对象,原子操作及基本步骤,如login

    login_dialog.rb文件中定义了代码登陆弹出框的LoginDialog。由于login dialog可能会出现在多个页面,比如qq音乐的登陆页面也有该弹出框,所以将其抽象成module,需要用到的页面直接include该module既可。

    login.rb文件调用page object并实现了具体的测试逻辑,这个文件中可以使用你熟悉的测试框架来组织用例,如unit test和rspec等

    关于Page Object设计模式的介绍就要告一段落了,从上面的代码中我们可以感觉到,Page Object模式加上测试用例框架就基本上可以等于简单的自动化测试框架了。所以在构建自动化测试框架的过程中,活用Page Object将为我们带来一系列的实惠和惊喜,就像超级的代金券一样,看上去不起眼但用起来却其乐无穷,浑身舒爽。

    代码链接如下

    page_object_attach

  • 相关阅读:
    MySQL的数据库,数据表,数据的操作
    数组函数
    字符串函数,时间函数,数学函数,数组
    PHP函数
    php类型的相关函数,运算符,条件判断,循环
    PHP数据类型
    vector中erase用法注意事项
    C++11 右值引用&&
    vector中find和find_if的用法 以后再遍历剁手!
    在cocos2d中添加自己的shader教程
  • 原文地址:https://www.cnblogs.com/nbkhic/p/2221508.html
Copyright © 2020-2023  润新知