我想将这个大型脚本分解为遵循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
答案 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)
您可以将场景分为三个部分。
More on Page-Object model。希望这会有所帮助。