PO模式-page object

PO模式学习思路

采用版本迭代的方式来学习,便于对不同版本的优缺点进行对比和理解
	1、V1:不使用任何涉及模式和单元测试框架
	2、V2:使用UnitTest管理用例
	3、V3:使用方法封装的思想,对代码进行优化
	4、V4:使用PO模式的分层思想对代码进行拆分
	5、V5:对PO分层之后的代码继续优化
	6、V6:PO模式深入封装,把共同操作提取封装到父类中,子类直接调用父类的方法

1、V1版本

不使用任何设计模式和单元测试框架。
每个文件里编写一个用例,完全的面向过程的编程方式。

1.1 存在的问题

1、一条测试用例对应一个文件,用例较多时不方便管理维护
2、代码高度冗余

1.2 示例代码

登录功能-账号不存

from selenium import webdriver
# 创建浏览器驱动对象,并完成初始化操作
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://localhost")
"""
登录功能-账号不存在
"""
# 点击首页的‘登录’链接,进入登录页面
driver.find_element_by_link_text("登录").click()
# 输入用户名
driver.find_element_by_id("username").send_keys("13099999999")
# 输入密码
driver.find_element_by_id("password").send_keys("123456")
# 输入验证码
driver.find_element_by_id("verify_code").send_keys("8888")
# 点击‘登录’按钮
driver.find_element_by_name("sbtbutton").click()
# 获取提示信息
msg = driver.find_element_by_class_name("layui-layer-content").text
print("msg=", msg)
# 关闭驱动对象
driver.quit()

登录功能-密码错误

from selenium import webdriver
# 创建浏览器驱动对象,并完成初始化操作
driver = webdriver.Firefox()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://localhost")
"""
登录功能-密码错误
"""
# 点击首页的‘登录’链接,进入登录页面
driver.find_element_by_link_text("登录").click()
# 输入用户名
driver.find_element_by_id("username").send_keys("13012345678")
# 输入密码
driver.find_element_by_id("password").send_keys("error")
# 输入验证码
driver.find_element_by_id("verify_code").send_keys("8888")
# 点击‘登录’按钮
driver.find_element_by_name("sbtbutton").click()
# 获取提示信息
msg = driver.find_element_by_class_name("layui-layer-content").text
print("msg=", msg)
# 关闭驱动对象
driver.quit()

2、V2版本

使用UnitTest管理用例,并断言用例的执行结果

2.1 引入UnitTest的好处

1、方便组织、管理多个测试用例
2、提供了丰富的断言方法
3、方便生成测试报告
4、减少了代码的冗余

2.2 存在的问题

代码冗余

2.3 示例代码

import unittest
from selenium import webdriver
class TestLogin(unittest.TestCase):
	"""
	对登录模块的功能进行测试
	"""
	@classmethod
	def setUpClass(cls):
		cls.driver = webdriver.Firefox()
		cls.driver.maximize_window()
		cls.driver.implicitly_wait(10)
		cls.driver.get("http://localhost")
	@classmethod
	def tearDownClass(cls):
		cls.driver.quit()
	def setUp(self):
		# 打开首页
		self.driver.get("http://localhost")
		# 点击首页的‘登录’链接,进入登录页面
		self.driver.find_element_by_link_text("登录").click()
	# 账号不存在
	def test_login_username_is_error(self):
		# 输入用户名
		self.driver.find_element_by_id("username").send_keys("13099999999")
		# 输入密码
		self.driver.find_element_by_id("password").send_keys("123456")
		# 输入验证码
		self.driver.find_element_by_id("verify_code").send_keys("8888")
		# 点击‘登录’
		self.driver.find_element_by_name("sbtbutton").click()
		# 断言提示信息
		msg = self.driver.find_element_by_class_name("layui-layer-content").text
		print("msg=", msg)
		self.assertIn("账号不存在", msg)

	# 密码错误
	def test_login_password_is_error(self):
		# 输入用户名
		self.driver.find_element_by_id("username").send_keys("13012345678")
		# 输入密码
		self.driver.find_element_by_id("password").send_keys("error")
		# 输入验证码
		self.driver.find_element_by_id("verify_code").send_keys("8888")
		# 点击‘登录’
		self.driver.find_element_by_name("sbtbutton").click()
		# 断言提示信息
		msg = self.driver.find_element_by_class_name("layui-layer-content").text
		print("msg=", msg)
		self.assertIn("密码错误", msg)

3、V3版本

3.1 方法封装

方法封装:是将一些有共性的或多次被使用的代码提取到一个方法中,供其他地方调用。

封装的好处:
	1、避免代码冗余
	2、容易维护
	3、隐藏代码实现的细节

目的:用最少的代码实现最多的功能

V3版本:使用方法封装的思想,对代码进行优化
	1、定义获取驱动对象的工具类
	2、封装“获取弹出框的提示信息等”

4、PO模式介绍

4.1 存在的问题

在做UI自动化时定位元素特别依赖页面,一旦页面发生变更就不得不跟着去修改定位元素的代码。

举例:假设要对一个元素进行点击操作,而且会经常对该元素进行操作,那么你就可能会编写多
处如下代码
driver.find_element_by_id("login-btn").click()


存在的问题:
	1、如果开发人员修改了这个元素的id,这时候你就不得不修改所有队形的代码
	2、存在大量的冗余代码

思考;
	如何来解决这个问题?

4.2 PO模式

PO是Page Object的缩写,PO模式是自动化测试项目开发实践的最佳设计模式之一。

核心思想是通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化, 
只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。

4.2.1 PO模式层级

PO模式可以把一个页面分为三层,对象库层、操作层、业务层。
	1、对象库层:封装定位元素的方法
	2、操作层:封装对元素的操作
	3、业务层:将一个或多个操作组合起来完成一个业务功能。比如登录:需要输入账号、密码、点击登录三个操作。

4.2.2 引入PO模式的好处

引入PO模式前:
	1、存在大量的冗余代码
	2、业务流程不清晰
	3、后期维护成本大

引入PO模式后:
	1、减少代码冗余
	2、业务代码和测试代码被分开,降低耦合性
	3、维护成本低

4.3 PO模式实践

V4版本:采用PO模式的分层思想对代码进行拆分

PO分层封装:
	1、base(基层):page页面的一些公共的方法;
		#Base类
			# 初始化方法
			# 查找元素方法
			# 点击元素方法
			# 输入方法
			# 获取文本方法
			# 截图方法
	
	注意:
		1、以上方法封装的时候,解包只需一处,在查找元素解包;
		2、driver为虚拟,谁调用base时,谁传入,无需关注从哪里来;
		3、loc:真正使用loc的方法只有在查找元素方法使用;
	
	2、page(页面对象):一个页面封装成一个对象
		应用:继承base;
		
		实现:
			1、模块名:page+实际操作模块名称  如:page_login.py
			2. 页面对象名:以大驼峰方法将模块名抄进来,有下划线去掉下划线
			3. 方法:涉及元素,将每个元素操作单独封装一个操作方法;
			4. 组装:根据需求组装以上操作步骤;

	3、scripts(业务层):导包调用 page页面
		实现:
			1. 模块:test+实际操作模块名称 如:test_login.py 
			2. 测试业务名称:以大驼峰方法将模块名抄进来,有下划线去掉下划线
			3. 方法:
					1. 初始化方法 setUp() 注:在unittest框架中不能使用def __init__()初始化方法;
						# 实例化 页面对象
						# 前置操作:如:打开等等
					2. 结束方法 teardown
						# 关闭驱动
					3. 测试方法
						# 根据要操作的业务来实现

扩展:
	loc变量:类型为元组,*loc为解包;
	

4.3.1 示例代码

目录
在这里插入图片描述


"""base.py  基类”“”

from time import strftime
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


class Base:
    # 1、初始化方法
    def __init__(self):
        # self.driver = driver

        # 获取driver
        self.driver = webdriver.Firefox()
        # 最大化浏览器
        self.driver.maximize_window()
        # 打开URL
        self.driver.get("https://10.66.210.198:8443")

    # 2和3有个共同的方法,查找元素方法
    def base_find_element(self, loc, timeout=30, poll=0.5):
        # 使用显示等待
        """

        :param loc: 元素的配置信息,格式为元组 如:login_link = By.PartialLink, "登录"
        :param timeout: 默认超时时间为30s,可以通过传入参数进行修改
        :param poll: 默认访问频率0.5s
        :return: 返回查找到的元素
        """
        return WebDriverWait(self.driver,
                             timeout=timeout,
                             poll_frequency=poll).until(lambda x: x.find_element(*loc))

    # 2、点击元素
    def base_click(self, loc):
        # 获取元素并进行点击
        self.base_find_element(loc).click()

    # 3、输入方法
    def base_input(self, loc, value):
        # 获取元素
        el = self.base_find_element(loc)
        # 清空
        el.clear()
        # 输入
        el.send_keys(value)

    # 4、获取文本信息
    def base_get_msg(self, loc):
        msg = self.base_find_element(loc).text

    # 5、截图
    def base_get_image(self):
        self.driver.get_screenshot_as_file("{}.png".format(strftime("%Y_%m_%d_%H_%M_%S")))

”“”loc变量   __init__.py“”“


from selenium.webdriver.common.by import By

"""以下为登录页面元素配置信息"""
# 用户名
login_username = By.CSS_SELECTOR, "#username"
# 密码
login_pwd = By.ID, "pass"
# 登录按钮
login_btn = By.CSS_SELECTOR, ".submit"
# 获取异常文本信息
login_error_msg = By.CSS_SELECTOR, ".error-info"

# print(type(login_btn))    <class 'tuple'>

“”“page.py  页面对象层”“”


from base.base import Base
import page


class PageLogin(Base):

    # 输入 用户名 封装
    def page_input_username(self, username):
        self.base_input(page.login_username, username)

    # 输入 密码 封装
    def page_input_pwd(self, pwd):
        self.base_input(page.login_pwd, pwd)

    # 点击 登录 封装
    def page_click_login_btn(self):
        self.base_click(page.login_btn)

    # 获取登录异常信息
    def page_error_login_msg(self):
        return self.base_get_msg(page.login_error_msg)

    # 截图
    def page_get_screenshot(self):
        self.base_get_image()

    # 组装 封装
    def page_login(self, username, pwd):
        self.page_input_username(username)
        self.page_input_pwd(pwd)
        self.page_click_login_btn()

”“”test_login.py 业务层“”“


import unittest
from time import sleep

from page.page_login import PageLogin
from base.base import Base
from parameterized import parameterized


def get_data():
    return [("admin", "123@123", "密码解密失败,请重新输入!!")]


# 新建测试类 并继承
class TestLogin(unittest.TestCase):

    # setUP
    def setUp(self):
        self.login = PageLogin()

    # tearDwon
    def tearDown(self):
        # 退出浏览器驱动
        sleep(5)
        self.login.driver.quit()

    # 登录测试方法
    @parameterized.expand(get_data())
    def test_login(self, username, pwd, expect_result):
        # 调用登录方法
        self.login.page_login(username, pwd)
        # 获取登录提示信息
        msg = self.login.page_error_login_msg()
        print(msg)
        try:
            # 断言
            self.assertEqual(msg, expect_result)
            sleep(5)
        except AssertionError:
            # 截图
            sleep(5)
            self.login.page_get_screenshot()

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

炫酷的腿毛!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值