PO模式
1、为什么需要PO思想?
首先我们观察和思考一下,目前我们写的作业脚本的问题: 元素定位和操作动 作写到一起了,这就就会用导致一个问题:
- UI的页面元素比较容易变化的,所以元素定位和脚本操作写到一起,一旦 元素发生了变化,这些脚本都需要修改
- 特别是登录页面在很多个页面都会涉及到 被引用,那么就会导致: 维护成 本比较大。 所以,我们通过PO模式来解决这个问题。
2、什么是PO模式
Page Object Model(页面对象模型), 或者也可称之为POM。在UI自动化测试 广泛使用的一种分层设计模式。
page就是网页的页面层,object就是封装的LoginPage 类:每一个页面一个 类,包含业务逻辑和测试对象
- 页面元素元素定位: 定义为类的属性
- 页面的操作行为 : 定义为类的方法
- 业务逻辑: 对页面的操作 为了得到实际结果的过程 和步骤 ,这是测试 对象
测试用例+ 测试数据层: 单独维护。 包含测试逻辑步骤和测试用例
- 测试逻辑: 测试部分 ,预期结果和实际结果的对比 ,这是测试用例
PO核心是通过页面层封装所有的页面元素及操作,测试用例层通过调用页面层 操作组装业务逻辑。
PO模式的核心思想:体现了业务逻辑和测试逻辑 的分离,测试用例和测试对象分离
以登录操作为例:
- 获取页面登录错误信息 登出 等操作都是业务逻辑,单独进行PageObject 的封装。
- 用例里只传用例数据,不会出现元素定位的代码。包括断言里也不要出现 元素定位。
PO模式最终的实现效果:
页面层:
- 页面类A(A1 A2 A3)
- 页面类B(B1 B2 B3) 页面类C(C1 C2 C3)
用例层:
- 用例1 = A1 + B2 + C3
- 用例2 = A1 + B2 + C1
PO模式优点:
- 提高测试用例的可读性
- 提高测试用例可维护性
- 减少代码重复
我们现在以登录页面为例:登录页面类
因为这个页面里有元素定位 + 元素操作: 把这些东西最好是封装在一起,方便 被用例层调用。
- 页面里的元素: 写成类的属性
- 用元组形式表示;因为后面显示等待都是用元组调用的;调用用self调 用 ;
- 并不需要一次性把所有元素都写出来,可以后续扩展
- 页面的操作: 封装成实例方法
- driver先没有,
- 可以作为参数 一些变化的数据也参数化
三层PO思想【BasePage封装思想】
但是这样写还有个问题:既然每个元素都用显示等待操作,每个页面都写一 遍太麻烦,冗余度太高了;而且每个页面都有一些其他的共同的操作,比如点 击,输入文本等;那么每个页面都重复写,是不是可以进一步优化呢?
- 这些每个页面都会调用的方法【等待,点击,输入文本等】,就属于公共 方法; BasePage 封装思想
- 用三层PO模式: 公共页面的内容单独提取出来封装:这也叫做 。封装成为一个BasePage类。
- 其他页面自己独有的元素和方法依然放在单独页面类里封装;需要用到 basepage类的内容的时候,如何实现?--- 类的继承实现
所以新建一个common的目录,建一个base_page的py文件,方所有的功能 方法。
- BasePage 用来存放所有页面类的公共部分,一些公共的操作(显式封装)
- 封装一个BasePage的类,里面的方法都是每个页面都要用的公共的方法。
base_page.py
from time import sleep
import pyautogui
import pyperclip
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains
class BasePage:
# driver每个方法都用,如果直接定位参数,没有办法实现实例方法共享,所以可以定义为实例属性 实现共享
def __init__(self,driver): # 初始化方法里定义了以参数 到时候实例化对象传参
"""初始化函数 定义driver作为实例属性 实例实例方法共享这个实例属性"""
self.driver = driver
# 操作和行为 行为为实例方法
def wait_element_clickable(self, locator):
web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.element_to_be_clickable(locator))
return web_element
def wait_element_visible(self, locator):
web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.visibility_of_element_located(locator))
return web_element
def wait_element_presence(self, locator):
web_element = WebDriverWait(self.driver, 8, 0.5).until(EC.presence_of_element_located(locator))
return web_element
# 1、普通点击操作 --关键字
def click_element(self,locator):
self.wait_element_clickable(locator).click()
# 2、鼠标点击操作
def mouse_click(self,locator):
# 先找到元素
element = self.wait_element_visible(locator)
# 用鼠标点击
ActionChains(self.driver).click(element).perform()
# 3、js点击操作
def js_click(self,locator):
# 先找到元素
element = self.wait_element_visible(locator)
# 用js传参点击
self.driver.execute_script("arguments[0].click()",element)
# 4、输入数据文本
def input_text(self,locator,text):
self.wait_element_visible(locator).send_keys(text)
# 5、获取元素的文本 : 拿到这个文本 返回这个文本
def get_text(self,locator):
return self.wait_element_visible(locator).text
# 6、获取元素的属性:拿到这个属性的值 设置为返回值
def get_attribute(self,locator,attr_name):
return self.wait_element_visible(locator).get_attribute(attr_name)
# 7、窗口切换
def switch_window(self,url):
# 先拿到所有的窗口句柄
handles = self.driver.window_handles
for win in handles:
if self.driver.current_url == url: # 判断当前的页面是否为想要的url地址
break
else:
self.driver.switch_to.window(win) # 如果不是 继续切换
# 8、移动鼠标
def move_mouse(self,locator):
element = self.wait_element_presence(locator)
ActionChains(self.driver).move_to_element(element).perform()
# 9、文件上传
def file_upload(self,file_path):
# 1、先复制路径
pyperclip.copy(file_path)
# 2、粘贴 -- hotkey 通过热键粘贴
pyautogui.hotkey("ctrl", "v")
pyautogui.press("enter", presses=2)
home_page_v1.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
class HomePage:
# 页面元素元素定位: 定义为类的属性 --元组
loc_login = (By.XPATH, '//a[text()="登录"]')
# 操作和行为 行为为实例方法
def wait_element_clickable(self,driver,locator):
web_element = WebDriverWait(driver, 8, 0.5).until(EC.element_to_be_clickable(locator))
return web_element
def wait_element_visible(self,driver,locator):
web_element = WebDriverWait(driver, 8, 0.5).until(EC.visibility_of_element_located(locator))
return web_element
def wait_element_presence(self,driver,locator):
web_element = WebDriverWait(driver, 8, 0.5).until(EC.presence_of_element_located(locator))
return web_element
# 点击首页里的登录链接按钮 打开登录页面 操作-- 定义为实例方法
def click_login_link(self,driver):
self.wait_element_visible(driver,self.loc_login).click()
test_login.py
"""
编写测试用例 执行测试用例 断言 的框架 - pytest框架
"""
from page_object.login_page_v1 import LoginPage
from page_object.home_page_v1 import HomePage
from selenium import webdriver
# pytest框架编写测试用例
def test_login():
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("http://mall.lemonban.com:3344/")
# 1、点击homepage 登录的链接 == 先实例化对象,再调用实例方法,实例方法要传参driver
HomePage().click_login_link(driver)
# 2、调用LoginPage里的login实例方法 执行登录操作== 先实例化对象,再调用实例方法,实例方法要传参
LoginPage().login(driver,"lemon_py","12345678")