我想学习如何将大型脚本制作成使用Page Object Model结构协同工作的较小脚本

时间:2015-09-03 03:11:24

标签: ruby-on-rails ruby selenium selenium-webdriver rspec

我想将这个大型脚本分解为遵循Page Object Model结构并相互协作的较小脚本。做这个的最好方式是什么?基本上我的脚本从CSV文件中提取登录凭据并登录,然后检查页面是否加载了正确的链接。

   require 'CSV'
   require 'selenium-webdriver'
   require 'rubygems'
   require 'rspec'
   require 'securerandom'

         # Base class for Login methods. Subclasses should override this behavior
         # where appropriate.

         class DataEmail <
           Struct.new(:email, :password) 
         end

           $i = 1
           $num = 3

         while $i < $num do
           read_data = CSV.read('emailcsv.csv')
           read_data.each do |line|
           words = line

           p = DataEmail.new
           p.email = words[0]
           user = p.email

           p.password = words[1]
           pass = p.password

           describe 'Merchantcentric' do
               before(:all) do
                   @browser = Selenium::WebDriver.for :firefox
                   @browser.get "https://meffff.com/session/new"
                   @wait = Selenium::WebDriver::Wait.new(:timeout => 10) 
               end

               it 'Verify that login dispays ' do
                   @wait.until{@browser.find_element(:id, 'email')}
                   @browser.find_element(:id, 'email').displayed?
                   #Verify login at merchant site
                   @browser.find_element(id: "email").send_keys user
                   @browser.find_element(id: "password").send_keys pass
                   @browser.find_element(:xpath, "//input[@class='button primary']").click
               end

               it 'Verify that Manage Offers is present in the side nav ' do
                   @wait.until {@browser.find_element(id:'nav-deals')}
                   @browser.find_element(id:'nav-deals').displayed?
               end

               it 'Verify that Redeem Vouchers is present in the side nav ' do
                   @wait.until {@browser.find_element(id:'nav-customers')}
                   @browser.find_element(id:'nav-deals').displayed?
               end

               it 'Verify that Engage Customers is present in the side nav ' do
                   @wait.until {@browser.find_element(id:'nav-engage')}
                   @browser.find_element(id:'nav-deals').displayed?
               end

               it 'Verify that View Payments is present in the side nav ' do
                   @wait.until {@browser.find_element(id:'nav-accounting')}
                   @browser.find_element(id:'nav-deals').displayed?
                   @browser.quit
              end

           end

          $i +=1 

       end
   end

2 个答案:

答案 0 :(得分:1)

这真是一个非常大的问题!页面对象模型有很多不同的方法 - 我将尝试给你一个与你发布的代码相匹配的例子。它不会覆盖你所需要的100%,但应该让你知道你可以做的事情。

首先,您需要抽象浏览器内容。您希望测试本身构建页面对象调用,而不是直接调用浏览器本身。这意味着您可以在不必编辑测试的情况下更改幕后的内容。

#
# The web browser.
#
module Browser
  #
  # Starts the browser.
  #
  # @return [Void]
  def self.start
    @browser ||= Selenium::WebDriver.for :chrome
  end

  #
  # Closes the browser.
  #
  # @return [Void]
  def self.close
    @browser.quit
  end

  private

  #
  # A catch all method to pass WebDriver requests through to our browser object.
  #
  # @return [Void]
  def self.method_missing(method, *arguments)
    @browser.send(method, *arguments)
  end
end

这为我们提供了与浏览器交谈的基本方式。我们可以启动和停止它,也可以通过这个模块运行Selenium WebDriver方法。

接下来是基本页面对象模块:

# Basic page object module. You will expand upon this a lot.
#
module PageObject
  #
  # Creates a class method that will return the selector and reference
  # of an element
  #
  # @param name [Symbol] name of the method
  # @param params [Hash<String>] element selector (key) and reference (value)
  def element(name, params)
    send(:define_method, name) do
      params
    end
  end

  #
  # Types into specified element.
  #
  # @param element [Hash] the element selector and reference
  # @param text [String] the text to type
  def type(element, text)
    Browser.find_element(element).send_keys text
  end

  #
  # Clicks on the specified element.
  #
  # @param element [Hash] the element selector and reference
  # @return [Void]
  def click(element)
    Browser.find_element(element).click
  end
end

接下来是一个示例页面对象。我已经使用了你登录页面的例子。

#
# Page object representing a log in page. This is the bit that will contain all
# methods related to specific pages.
#
class LogInPage
  include PageObject

  element :email_field, id: 'email'
  element :password_field, id: 'password'
  element :submit_button, css: '.button.primary'

  #
  # Logs in using the specified user credentials.
  #
  # @param email [String] the users email address
  # @param password [String] the users password
  # @return [Void]
  def log_in_with!(email, password)
    type email_field, text: email
    type password_field, text: password
    click submit_button
    # Some approaches will return another page object at this point.
    # You can do this if you want, but personally I think it leads to confusing
    # tests.
  end

  #
  # Checks if a user is logged in.
  #
  # @return [Boolean]
  def user_logged_in?
    # This should return true or false depending on if a user is logged in.
    # Login page may not be the correct place for this, but it'll do for this
    # example.
  end
end

您的测试可能类似于:

describe 'Merchantcentric' do
  context 'User Login' do
    before :all do
      Browser.start
      Browser.get 'https://meffff.com/session/new'
      # Ideally all this browser creation stuff would be abstracted
      # out of your tests and into something else (spec_helper maybe?)
      @page = LogInPage.new
    end

    after :all do
      Browser.close
    end

    it 'success' do
      # I purposely left out your csv stuff - you should probably
      # have that separate to your tests also. I am assuming that
      # user and pass will be coming from somewhere.
      @page.log_in_with! user, pass
      assert(@page.user_logged_in?, 'User was not logged in successfully')
    end
  end
end

这些显然都在不同的文件中!我现在已经用完了午休时间,所以如果你有任何问题让我知道,我总是可以聊聊这些东西!如果这些东西中的某些东西不起作用也道歉...我没有测试就写了它但应该可以(ish)!

此外,在与它们交互之前,您不需要检查元素存在。如果元素不存在,那么测试应该以Selenium异常失败。

是什么让你满足于Rspec的功能性浏览器测试btw?你有没有探索其他选择,比如黄瓜?

更新

关于你的评论:

在您给出的示例中,您可以在PageObject类中创建一个名为element_exists?的新方法,并在LogInPage类(例如)中创建一个名为deals_present?的方法。你可以在断言中使用这种方法。

您的element_exists?方法很简单:

#
# Checks if an element exists, returning true or false.
#
# @params params [Hash] the selector and reference for the element
# @return [Boolean]
def element_exists?(params)
  Browser.find_elements(params).any?
end

您的'deals_present'方法可能类似于:

#
# Checks if the deals element is present on the page.
#
# @return [Boolean]
def deals_present?
  element_exists? deals_navigation
end

您显然需要使用我帖子中描述的'element'方法创建deals_navigation页面元素。

我注意到在检查deals元素之前你想要一个wait.until。同样,你会想要将它抽象到你的PageObject类中,然后在你的deals_present中调用它吗?方法。我会让你解决这个问题,但如果你有太多麻烦,请告诉我。

完成所有这些操作的测试断言看起来像是:

expect(!@page.deals_present?)

如果显示deals元素,则会失败。这样做的好处是你从测试本身中删除了大部分逻辑,这意味着如果逻辑发生变化,你只需要在一个地方而不是所有使用它的测试中更新它。

答案 1 :(得分:0)

您可以将场景分为三个部分。

  • 为库等所有通用功能创建新文件。记下用于从.csv文件中提取数据的通用函数。
  • 使用上述通用功能创建测试套件以验证登录功能。
  • 创建测试套件以检查页面是否加载了正确的链接。

More on Page-Object model。希望这会有所帮助。