web自动化05-PO模式-V4-V6

PO模式介绍
1. 深入理解PO模式的思想
2. 熟练掌握PO模式的分层思想
 
1. 存在的问题
在做UI自动化时定位元素特别依赖页面,一旦页面发生变更就不得不跟着去修改定位元素的代码。
举例:假设要对一个元素进行点击操作,而且会经常对该元素进行操作,那么你就可能会编写多
处如下代码
driver.find_element_by_id("login-btn").click()

 

存在的问题:
  • 如果开发人员修改了这个元素的id,这时候你就不得不修改所有对应的代码
  • 存在大量冗余代码
思考:如何来解决这个问题呢?
2. PO模式
PO是Page Object的缩写,PO模式是自动化测试项目开发实践的最佳设计模式之一。
核心思想是通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化, 只
需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。
 
PO模式可以把一个页面分为三层,对象库层、操作层、业务层。
  • 对象库层:封装定位元素的方法。
  • 操作层:封装对元素的操作。
  • 业务层:将一个或多个操作组合起来完成一个业务功能。比如登录:需要输入帐号、密码、点击登录三个操作。
2.1 引入PO模式的好处
引入PO模式前
    • 存在大量冗余代码
    • 业务流程不清晰
    • 后期维护成本大
引入PO模式后
    • 减少冗余代码
    • 业务代码和测试代码被分开,降低耦合性
    • 维护成本低
PO模式实践
1. V4版本
采用PO模式的分层思想对代码进行拆分
1.1 PO分层封装
对登录页面进行分层封装:
  • 对象库层:LoginPage
  • 操作层:LoginHandle
  • 业务层:LoginProxy
调用业务层的方法,编写测试用例:
  • 测试用例:TestLogin
1.2 示例代码
from po.utils import DriverUtil
class LoginPage:
"""
对象库层
"""
def __init__(self):
self.driver = DriverUtil.get_driver()
# 用户名输入框
self.username = None
# 密码
self.password = None
# 验证码输入框
self.verify_code = None
# 登录按钮
self.login_btn = None
# 忘记密码
self.forget_pwd = None
def find_username(self):
return self.driver.find_element_by_id("username")
def find_password(self):
return self.driver.find_element_by_id("password")
def find_verify_code(self):
return self.driver.find_element_by_id("verify_code")
def find_login_btn(self):
return self.driver.find_element_by_name("sbtbutton")
def find_forget_pwd(self):
return self.driver.find_element_by_partial_link_text("忘记密码")
class LoginHandle:
"""
操作层
"""
def __init__(self):
self.login_page = LoginPage()
def input_username(self, username):
self.login_page.find_username().send_keys(username)
def input_password(self, pwd):
self.login_page.find_password().send_keys(pwd)
def input_verify_code(self, code):
self.login_page.find_verify_code().send_keys(code)
def click_login_btn(self):
self.login_page.find_login_btn().click()
def click_forget_pwd(self):
self.login_page.find_forget_pwd().click()
class LoginProxy:
"""
业务层
"""
def __init__(self):
self.login_handle = LoginHandle()
# 登录
def login(self, username, password, verify_code):
# 输入用户名
self.login_handle.input_username(username)
# 输入密码
self.login_handle.input_password(password)
# 输入验证码
self.login_handle.input_verify_code(verify_code)
# 点击登录按钮
self.login_handle.click_login_btn()
# 跳转到忘记密码页面
def to_forget_pwd_page(self):
# 点击忘记密码
self.login_handle.click_forget_pwd()
import unittest
from po import utils
from po.utils import DriverUtil
from po.v4.page.login_page import LoginProxy
class TestLogin(unittest.TestCase):
"""
对登录模块的功能进行测试
"""
@classmethod
def setUpClass(cls):
cls.driver = DriverUtil.get_driver()
cls.login_proxy = LoginProxy()
@classmethod
def tearDownClass(cls):
DriverUtil.quit_driver()
def setUp(self):
# 打开首页
self.driver.get("http://localhost")
# 点击首页的‘登录’链接,进入登录页面
self.driver.find_element_by_link_text("登录").click()
# 账号不存在
def test_login_username_is_error(self):
self.login_proxy.login("13099999999", "123456", "8888")
# 断言提示信息
msg = utils.get_tips_msg()
print("msg=", msg)
self.assertIn("账号不存在", msg)
# 密码错误
def test_login_password_is_error(self):
self.login_proxy.login("13012345678", "123456", "8888")
# 断言提示信息
msg = utils.get_tips_msg()
print("msg=", msg)
self.assertIn("密码错误", msg)

 

2. V5版本
对PO分层之后的代码继续优化
1. 优化对象库层的代码,抽取元素的定位方式,把定位信息定义在对象的属性中,便于集中管理
2. 优化操作层的代码,针对输入操作应该先清空输入框中的内容再输入新的内容
 
from selenium.webdriver.common.by import By
from po.utils import DriverUtil
class LoginPage:
"""
对象库层
"""
def __init__(self):
self.driver = DriverUtil.get_driver()
# 用户名
self.username = (By.ID, "username")
# 密码
self.password = (By.ID, "password")
# 验证码输入框
self.verify_code = (By.ID, "verify_code")
# 登录按钮
self.login_btn = (By.NAME, "sbtbutton")
# 忘记密码
self.forget_pwd = (By.PARTIAL_LINK_TEXT, "忘记密码")
def find_username(self):
return self.driver.find_element(self.username[0], self.username[1])
def find_password(self):
return self.driver.find_element(self.password[0], self.password[1])
def find_verify_code(self):
return self.driver.find_element(self.verify_code[0], self.verify_code[1])
def find_login_btn(self):
return self.driver.find_element(self.login_btn[0], self.login_btn[1])
def find_forget_pwd(self):
return self.driver.find_element(self.forget_pwd[0], self.forget_pwd[1])


class LoginHandle:
"""
操作层
"""
def __init__(self):
self.login_page = LoginPage()
def input_username(self, username):
self.login_page.find_username().clear()
self.login_page.find_username().send_keys(username)
def input_password(self, pwd):
self.login_page.find_password().clear()
self.login_page.find_password().send_keys(pwd)
def input_verify_code(self, code):
self.login_page.find_verify_code().clear()
self.login_page.find_verify_code().send_keys(code)
def click_login_btn(self):
self.login_page.find_login_btn().click()
def click_forget_pwd(self):
self.login_page.find_forget_pwd().click()
class LoginProxy:
"""
业务层
"""
def __init__(self):
self.login_handle = LoginHandle()
# 登录
def login(self, username, password, verify_code):
# 输入用户名
self.login_handle.input_username(username)
# 输入密码
self.login_handle.input_password(password)
# 输入验证码
self.login_handle.input_verify_code(verify_code)
# 点击登录按钮
self.login_handle.click_login_btn()
# 跳转到忘记密码页面
def to_forget_pwd_page(self):
# 点击忘记密码
self.login_handle.click_forget_pwd()

 

 

PO模式深入封装
1. 能够采用继承的思想对PO模式进行深入的封装
1. V6版本
把共同操作提取封装到父类中,子类直接调用父类的方法,避免代码冗余
1. 对象库层-基类,把定位元素的方法定义在基类中
2. 操作层-基类,把对元素执行输入操作的方法定义在基类中
1.1 示例代码
# base_page.py
from po.utils import DriverUtil
class BasePage:
"""
基类-对象库层
"""
def __init__(self):
self.driver = DriverUtil.get_driver()
def find_element(self, location):
return self.driver.find_element(location[0], location[1])
class BaseHandle:
"""
基类-操作层
"""
def input_text(self, element, text):
"""
在输入框里输入文本内容,先清空再输入
:param element: 要操作的元素
:param text: 要输入的文本内容
"""
element.clear()
element.send_keys(text)

 

from selenium.webdriver.common.by import By
from po.v6.common.base_page import BasePage, BaseHandle
class LoginPage(BasePage):
"""
对象库层
"""
def __init__(self):
super().__init__()
# 用户名输入框
self.username = (By.ID, "username")
# 密码
self.password = (By.ID, "password")
# 验证码
self.verify_code = (By.ID, "verify_code")
# 登录按钮
self.login_btn = (By.NAME, "sbtbutton")
# 忘记密码
self.forget_pwd = (By.PARTIAL_LINK_TEXT, "忘记密码")
def find_username(self):
return self.find_element(self.username)
def find_password(self):
return self.find_element(self.password)
def find_verify_code(self):
return self.find_element(self.verify_code)
def find_login_btn(self):
return self.find_element(self.login_btn)
def find_forget_pwd(self):
return self.find_element(self.forget_pwd)
class LoginHandle(BaseHandle):
"""
操作层
"""
def __init__(self):
self.login_page = LoginPage()
def input_username(self, username):
self.input_text(self.login_page.find_username(), username)
def input_password(self, pwd):
self.input_text(self.login_page.find_password(), pwd)
def input_verify_code(self, code):
self.input_text(self.login_page.find_verify_code(), code)
def click_login_btn(self):
self.login_page.find_login_btn().click()
def click_forget_pwd(self):
self.login_page.find_forget_pwd().click()


class LoginProxy:
"""
业务层
"""
def __init__(self):
self.login_handle = LoginHandle()
# 登录
def login(self, username, password, verify_code):
# 输入用户名
self.login_handle.input_username(username)
# 输入密码
self.login_handle.input_password(password)
# 输入验证码
self.login_handle.input_verify_code(verify_code)
# 点击登录按钮
self.login_handle.click_login_btn()
# 跳转到忘记密码页面
def to_forget_pwd_page(self):
# 点击忘记密码
self.login_handle.click_forget_pwd()

 

 
 
posted @ 2019-07-10 22:32  snailon  阅读(766)  评论(0)    收藏  举报