1.简单的po模式
po模式,简单来说,就是page object(页面对象)
我们做ui自动化的时候,会遇到很多的页面
为了维护方面,我们可以将每一个页面封装成一个class
po模式,其实没有任何新的知识点融入,只不过是将面向对象的思想融入进来了
基本代码示例(简单的po模式)
from selenium import webdriver
class loginPage:
def __init__(self, url):
# 创建浏览器驱动对象,这里是打开浏览器
self.driver = webdriver.Chrome("E:\愤怒吧小鸟\chromedriver-win64\chromedriver.exe")
# 访问网址
self.driver.get(url)
#用户名输入框
self.user_name_input=self.driver.find_element_by_name("username")
#密码输入框
self.password_input=self.driver.find_element_by_name("password")
#登录按钮
self.login_button=self.driver.find_element_by_css_selector("button")
def logn(self):
self.user_name_input.send_keys("libai")
self.password_input.send_keys("opmsopms123")
self.login_button.click()
LP = loginPage("http://127.0.0.1:8088/")
LP.logn()
注意:上段代码如果加上一个界面刷新的操作,就会报错(陈旧的元素引用异常),selenium关于StaleElementReferenceException异常的出现以及解决方案可参考以下链接:
web自动化异常,selenium.common.exceptions.StaleElementReferenceException(陈旧的元素引用异常)-CSDN博客
即为了避免“StaleElementReferenceException异常”,代码改为如下模式:
更改的思路可参考上面的链接
from selenium import webdriver
class loginPage:
def __init__(self, url):
# 创建浏览器驱动对象,这里是打开浏览器
self.driver = webdriver.Chrome("E:\愤怒吧小鸟\chromedriver-win64\chromedriver.exe")
# 访问网址
self.driver.get(url)
# 用户名输入框
def user_name_input_box(self):
return self.driver.find_element_by_name("username")
# 密码输入框
def password_input_box(self):
return self.driver.find_element_by_name("password")
# 登录按钮
def login_button_box(self):
return self.driver.find_element_by_css_selector("button")
def logn(self):
self.driver.refresh()
self.user_name_input_box().send_keys("libai") #封装成方法,元素赋值和操作元素中间就不会有界面刷新了
self.password_input_box().send_keys("opmsopms123")
self.login_button_box().click()
LP = loginPage("http://127.0.0.1:8088/")
LP.logn()
2.优化po模式的代码-初级版本
第一步骤的代码的优化(是避免了taleElementReferenceException异常之后的代码优化)
思路:(1)driver路径,url等抽离出来,形成一个配置文件-setting.py
(2)把driver抽离出来(公用的)-myDriver.py
(3)页面模块-loginPage.py
项目构造:
day06-初级版本-setting.py
-myDriver.py
-loginPage.py
setting.py代码
# 域名
url = "http://127.0.0.1:8088/"
# driver 路径
drverPath = {
"Chrome": "E:\愤怒吧小鸟\chromedriver-win64\chromedriver.exe",
"Firefox": ""
}
user_name = "libai"
password = "opmsopms123"
myDriver.py代码:
from selenium import webdriver
driver = webdriver.Chrome("E:\愤怒吧小鸟\chromedriver-win64\chromedriver.exe")
思考:如何保证driver唯一;
解决方法:定义一个工具类
思考:如何再类中让变量保持唯一呢(可用类方法的变量)
myDriver.py优化后的代码
from day06.初级版本.setting import drverPath, url
from selenium import webdriver
class Driver:
"""
任何地方需要用到浏览器驱动对象,直接调用此类下的 get_driver函数,获取返回值即可
且 get_driver 函数保证了我们的 driver 唯一
类成员存放在类中,是唯一的,普通成员存放在对象中,不是唯一的;
所以把driver成员创建为类成员(加了一个注解@classmethod)
"""
# 初始化为空
driver = None
@classmethod
def get_driver(cls, browser_name):
# 如果 cls.driver 为 None,则证明不存在,进入if代码块去创建
# 如果 cls.driver 不为 None,则证明存在,不需要进入if代码块创建,可直接返回
if cls.driver is None: #cls.driver是唯一的
if browser_name == "Chrome":
cls.driver = webdriver.Chrome(drverPath["Chrome"])
elif browser_name == "Firefox":
cls.driver = webdriver.Firefox(drverPath["Firefox"])
# 最大化窗口,
cls.driver.maximize_window()
# 访问默认的网页
cls.driver.get(url)
return cls.driver
if __name__ == '__main__':
#调用两次get_driver,只会打开一次浏览器,说明代码逻辑正确(因为把driver成唯一的)
Driver.get_driver("Chrome")
Driver.get_driver("Chrome")
运行结果:
只会打开一次浏览器
loginPage.py代码:
from day06.初级版本.myDriver import Driver
from day06.初级版本.setting import user_name, password
class LoginPage:
def __init__(self):
# 创建浏览器驱动对象,这里是打开浏览器
self.driver = Driver.get_driver("Chrome")
# 用户名输入框
def user_name_input_box(self):
return self.driver.find_element_by_name("username")
# 密码输入框
def password_input_box(self):
return self.driver.find_element_by_name("password")
# 登录按钮
def login_button_box(self):
return self.driver.find_element_by_css_selector("button")
class LoginPageAction(LoginPage):
"""
我们一般会将页面当中一些常用的动作,会重复使用的动作
抽离出来,写成一个个的函数,封装在一个页面动作类当中
页面动作类,继承对应的页面类
"""
def logn(self):
self.driver.refresh()
self.user_name_input_box().send_keys(user_name)
self.password_input_box().send_keys(password)
self.login_button_box().click()
"""
将页面分为 页面元素类,与页面动作类
若以后,只是逻辑修改了,只需要改动动作类即可,而不必关系元素定位
若是元素定位发生了变化,只需要修改页面元素类即可,执行动作、逻辑,都不必关心
"""
if __name__ == '__main__':
LPA = LoginPageAction()
LPA.logn()
运行结果:
界面可登录成功
3.优化po模式的代码-抽离出basepage版本
思考:界面肯定不止一个,所以新增一个basePage.py模块,该模块是将页面通用的方法,抽离出来封装起来
结构如下图
setting.py和myDriver跟初级版本一样,代码没变
basePage.py代码
from day06.抽离出basepage的版本.myDriver import Driver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from day06.抽离出basepage的版本.setting import time_out, poll_time
class BasePage:
"""
basepage 是将一些页面通用的方法,抽离出来封装起来
"""
def __init__(self):
# 获取浏览器驱动对象
self.driver = Driver.get_driver("Chrome")
def get_element(self, locator):
"""
显示等待,查找元素
:param locator: 要求传入的参数是一个元组,表示元素定位方法和表达式
:return: 单个的元素对象
"""
WebDriverWait(
# 传入浏览器对象
driver=self.driver,
# 传入超时时间
timeout=time_out,
# 设置轮询时间
poll_frequency=poll_time).until(
EC.visibility_of_element_located(locator)
)
return self.driver.find_element(*locator) # *locator是解包
def get_elements(self, locator):
"""
显示等待,查找元素
:param locator: 要求传入的参数是一个元组,表示元素定位方法和表达式
:return: 元素列表
"""
WebDriverWait(
# 传入浏览器对象
driver=self.driver,
# 传入超时时间
timeout=time_out,
# 设置轮询时间
poll_frequency=poll_time).until(
EC.visibility_of_element_located(locator)
)
return self.driver.find_elements(*locator)
解包的概念,参考以下链接
python之打包、解包_python中打包和解包的区别-CSDN博客
loginPage.py代码
from day06.抽离出basepage的版本.basePage import BasePage
from day06.初级版本.setting import user_name, password
from selenium.webdriver.common.by import By
class LoginPage(BasePage):
# 用户名输入框
def user_name_input_box(self):
return self.driver.find_element_by_name("username")
# 密码输入框
def password_input_box(self):
#假设密码输入框按钮需要用到显示等待,将代码改成如下所示即可
return self.get_element((By.NAME, "password"))
# 登录按钮
def login_button_box(self):
return self.driver.find_element_by_css_selector("button")
class LoginPageAction(LoginPage):
"""
我们一般会将页面当中一些常用的动作,会重复使用的动作
抽离出来,写成一个个的函数,封装在一个页面动作类当中
页面动作类,继承对应的页面类
"""
def logn(self):
self.driver.refresh()
self.user_name_input_box().send_keys(user_name)
self.password_input_box().send_keys(password)
self.login_button_box().click()
"""
将页面分为 页面元素类,与页面动作类
若以后,只是逻辑修改了,只需要改动动作类即可,而不必关系元素定位
若是元素定位发生了变化,只需要修改页面元素类即可,执行动作、逻辑,都不必关心
"""
if __name__ == '__main__':
LPA = LoginPageAction()
LPA.logn()
结果:
界面登录成功
注意:如需要代码,可在资源绑定中自行下载