当前位置: 首页 > news >正文

六、UI自动化测试06--PO设计模式

目录

  • 一、PO 设计模式
    • 1. v1 版本
      • 1.1 v1.1
      • 1.2 v1.2
    • 2. v2 版本
    • 3. ⽅法封装套路
    • 4. v3 版本
      • 4.1 浏览器对象管理类的实现
      • 4.2 浏览器对象管理类的优化
      • 4.3 浏览器对象管理类的使⽤
      • 4.4 获取弹窗信息⽅法的封装
    • 5. PO 设计模式
    • 6. v4 版本
      • 6.1 PO⻚⾯元素封装步骤
      • 6.2 测试⽤例的最终代码样式
    • 7. v5 版本
      • 7.1 PO ⽂件对象层代码优化
      • 7.2 PO ⽂件操作层代码优化

一、PO 设计模式

1. v1 版本

说明: 通过测试执⾏框架 pytest, 可以整合所有的同⼀模块的测试⽤例脚本, 并且需要尽⼒符合⼿⼯测试的操作业务逻辑, 最终实现执⾏单个测试脚本, 执⾏同⼀模块的所有测试⽤例

1.1 v1.1

  • 通过测试⽅法整合测试脚本
"""
整合多个脚本⾄同⼀个测试⽤例中
"""
import pytest
from time import sleep
from selenium import webdriverclass TestLogin(object):"""登录测试类"""def test_account_does_not_exist(self):"""账号不存在测试⽅法"""driver = webdriver.Chrome()driver.get('http://127.0.0.1/')driver.maximize_window() # 窗⼝最⼤化driver.implicitly_wait(10) # 隐式等待# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯driver.find_element_by_link_text('登录').click()# 2. 输⼊⼀个不存在的⽤户名driver.find_element_by_id('username').send_keys('13811110001')# 3. 输⼊密码driver.find_element_by_id('password').send_keys('123456')# 4. 输⼊验证码driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textmsg = driver.find_element_by_class_name('layui-layercontent').textprint('错误信息为:', msg)sleep(3)driver.quit()def test_wrong_password(self):"""密码错误测试⽅法"""driver = webdriver.Chrome()driver.get('http://127.0.0.1/')driver.maximize_window() # 窗⼝最⼤化driver.implicitly_wait(10) # 隐式等待# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯driver.find_element_by_link_text('登录').click()# 2. 输⼊⽤户名driver.find_element_by_id('username').send_keys('13800001111')# 3. 输⼊错误密码driver.find_element_by_id('password').send_keys('error')# 4. 输⼊验证码driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textmsg = driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)sleep(3)driver.quit()if __name__ == '__main__':pytest.main(['-s', 'tpshop_login_1.py'])

1.2 v1.2

  • 通过 setup() 和 teardown() ⽅法优化多个脚本相同的前置后后置操作
"""
整合多个脚本⾄同⼀个测试⽤例中
"""
import pytest
from time import sleep
from selenium import webdriverclass TestLogin(object):"""登录测试类"""def setup(self):self.driver = webdriver.Chrome()self.driver.get('http://127.0.0.1/')self.driver.maximize_window() # 窗⼝最⼤化self.driver.implicitly_wait(10) # 隐式等待def teardown(self):sleep(3)self.driver.quit()def test_account_does_not_exist(self):"""账号不存在测试⽅法"""# driver = webdriver.Chrome()# driver.get('http://127.0.0.1/')# driver.maximize_window() # 窗⼝最⼤化# driver.implicitly_wait(10) # 隐式等待# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯self.driver.find_element_by_link_text('登录').click()# 2. 输⼊⼀个不存在的⽤户名self.driver.find_element_by_id('username').send_keys('13811110001')# 3. 输⼊密码self.driver.find_element_by_id('password').send_keys('123456')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)# sleep(3)# driver.quit()def test_wrong_password(self):"""密码错误测试⽅法"""# driver = webdriver.Chrome()# driver.get('http://127.0.0.1/')# driver.maximize_window() # 窗⼝最⼤化# driver.implicitly_wait(10) # 隐式等待# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯self.driver.find_element_by_link_text('登录').click()# 2. 输⼊⽤户名self.driver.find_element_by_id('username').send_keys('138000011
11')# 3. 输⼊错误密码self.driver.find_element_by_id('password').send_keys('error')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layuilayer-content').textprint('错误信息为:', msg)# sleep(3)# driver.quit()if __name__ == '__main__':pytest.main(['-s', 'tpshop_login_2.py'])

2. v2 版本

  • 通过 setup_class() 和 teardown_class() ⽅法使脚本的执⾏逻辑跟符合⼿⼯测试逻辑
"""
整合多个脚本⾄同⼀个测试⽤例中
"""
import pytest
from time import sleep
from selenium import webdriverclass TestLogin(object):"""登录测试类"""def setup_class(self):self.driver = webdriver.Chrome()self.driver.get('http://127.0.0.1/')self.driver.maximize_window() # 窗⼝最⼤化self.driver.implicitly_wait(10) # 隐式等待def teardown_class(self):sleep(3)self.driver.quit()def setup(self):# self.driver = webdriver.Chrome()# self.driver.get('http://127.0.0.1/')# self.driver.maximize_window() # 窗⼝最⼤化# self.driver.implicitly_wait(10) # 隐式等待# 打开⾸⻚self.driver.get('http://127.0.0.1/')# 点击登录self.driver.find_element_by_link_text('登录').click()def teardown(self):# sleep(3)# self.driver.quit()passdef test_account_does_not_exist(self):"""账号不存在测试⽅法"""# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯# self.driver.find_element_by_link_text('登录').click()# 2. 输⼊⼀个不存在的⽤户名self.driver.find_element_by_id('username').send_keys('13811110001')# 3. 输⼊密码self.driver.find_element_by_id('password').send_keys('123456')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)def test_wrong_password(self):"""密码错误测试⽅法"""# 1. 点击⾸⻚的‘登录’链接,进⼊登录⻚⾯# self.driver.find_element_by_link_text('登录').click()# 2. 输⼊⽤户名self.driver.find_element_by_id('username').send_keys('13800001111')# 3. 输⼊错误密码self.driver.find_element_by_id('password').send_keys('error')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)if __name__ == '__main__':pytest.main(['-s', 'tpshop_login_3.py'])

3. ⽅法封装套路

    1. 确定⽅法的存放位置: 找位置
    1. 给⽅法起个合适名字: 起名字
    1. 放⼊要封装的代码内容: 放代码
    1. 确认是否需要参数和返回值: 确必要
    1. 调⽤封装好的⽅法使⽤: 做调⽤

4. v3 版本

4.1 浏览器对象管理类的实现

"""
公共⽅法模块: 习惯命名 utils
"""
from selenium import webdriver
from time import sleepclass DriverUtil(object):"""浏览器对象管理类"""driver = None # 浏览器对象变量初始化状态def get_driver(self):"""获取浏览器对象⽅法"""# 说明: 为了防⽌在同⼀次测试过程中, 调⽤获取浏览器对象⽅法时,# 都会创建⼀个新的浏览器对象, 因此有必要先判断对象是否存在, 
不存在时在创建!if self.driver is None:self.driver = webdriver.Chrome()self.driver.get('http://127.0.0.1/')self.driver.maximize_window() # 窗⼝最⼤化self.driver.implicitly_wait(10) # 隐式等待return self.driverdef quit_driver(self):"""退出浏览器对象⽅法"""# 说明: 必须保证浏览器对象存在, 才能执⾏退出操作if self.driver: # 等价于: if self.driver is not None:sleep(3)self.driver.quit()self.driver = None # 保险⼿段: 移除对象后, 保留对象变量, 以备下⼀次使⽤if __name__ == '__main__':my_driver = DriverUtil()my_driver.get_driver()sleep(2)my_driver.quit_driver()

4.2 浏览器对象管理类的优化

"""
公共⽅法模块: 习惯命名 utils
"""
from selenium import webdriver
from time import sleepclass DriverUtil(object):"""浏览器对象管理类"""# 说明: 对象变量只需要在类定义内部使⽤, 因此定义为私有__driver = None # 浏览器对象变量初始化状态@classmethoddef get_driver(cls):"""获取浏览器对象⽅法"""# 说明: 为了防⽌在同⼀次测试过程中, 调⽤获取浏览器对象⽅法时,# 都会创建⼀个新的浏览器对象, 因此有必要先判断对象是否存在, 不存在时在创建!if cls.__driver is None:cls.__driver = webdriver.Chrome()cls.__driver.get('http://127.0.0.1/')cls.__driver.maximize_window() # 窗⼝最⼤化cls.__driver.implicitly_wait(10) # 隐式等待return cls.__driver@classmethoddef quit_driver(cls):"""退出浏览器对象⽅法"""# 说明: 必须保证浏览器对象存在, 才能执⾏退出操作if cls.__driver: # 等价于: if self.driver is not None:sleep(3)cls.__driver.quit()cls.__driver = None # 保险⼿段: 移除对象后, 保留对象变量, 以备下⼀次使⽤if __name__ == '__main__':# my_driver = DriverUtil()# my_driver.get_driver()# sleep(2)# my_driver.quit_driver()# 说明: 定义为类⽅法, 可以直接由类对象调⽤, 省略实例化对象步骤DriverUtil.get_driver() # 获取浏览器对象sleep(2)DriverUtil.quit_driver() # 退出浏览器对象

4.3 浏览器对象管理类的使⽤

"""
整合多个脚本⾄同⼀个测试⽤例中
"""
import pytest
from time import sleep
from selenium import webdriverfrom utils import DriverUtilclass TestLogin(object):"""登录测试类"""def setup_class(self):# self.driver = webdriver.Chrome()# self.driver.get('http://127.0.0.1/')# self.driver.maximize_window() # 窗⼝最⼤化# self.driver.implicitly_wait(10) # 隐式等待self.driver = DriverUtil.get_driver() # 获取浏览器对象def teardown_class(self):# sleep(3)# self.driver.quit()DriverUtil.quit_driver() # 退出浏览器对象def setup(self):# 打开⾸⻚self.driver.get('http://127.0.0.1/')# 点击登录self.driver.find_element_by_link_text('登录').click()def teardown(self):passdef test_account_does_not_exist(self):"""账号不存在测试⽅法"""# 2. 输⼊⼀个不存在的⽤户名self.driver.find_element_by_id('username').send_keys('13811110001')# 3. 输⼊密码self.driver.find_element_by_id('password').send_keys('123456')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)def test_wrong_password(self):"""密码错误测试⽅法"""# 2. 输⼊⽤户名self.driver.find_element_by_id('username').send_keys('13800001111')# 3. 输⼊错误密码self.driver.find_element_by_id('password').send_keys('error')# 4. 输⼊验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息# 获取元素⽂本值: 元素对象.textsleep(2)msg = self.driver.find_element_by_class_name('layui-layer-content').textprint('错误信息为:', msg)if __name__ == '__main__':pytest.main(['-s', 'tpshop_login_3.py'])

4.4 获取弹窗信息⽅法的封装

from selenium import webdriver
from time import sleepdef get_alert_msg():"""获取弹窗信息⽅法"""sleep(2)# msg = self.driver.find_element_by_class_name('layui-layercontent').text# msg = driver.find_element_by_class_name('layui-layercontent').textmsg = 
DriverUtil.get_driver().find_element_by_class_name('layui-layer-content').textreturn msgclass DriverUtil(object):"""浏览器对象管理类"""__driver = None # 浏览器对象变量初始化状态@classmethoddef get_driver(cls):"""获取浏览器对象⽅法"""if cls.__driver is None:cls.__driver = webdriver.Chrome() # 浏览器类型cls.__driver.get('http://127.0.0.1/') # 项⽬地址cls.__driver.maximize_window() # 窗⼝最⼤化cls.__driver.implicitly_wait(10) # 隐式等待return cls.__driver@classmethoddef quit_driver(cls):"""退出浏览器对象⽅法"""if cls.__driver:sleep(3)cls.__driver.quit()cls.__driver = Noneif __name__ == '__main__':DriverUtil.get_driver() # 获取浏览器对象sleep(2)DriverUtil.quit_driver() # 退出浏览器对象

5. PO 设计模式

  • 说明: PO 模式⼜可以叫 POM(P:Page O:Object), 是 UI ⾃动化测试中⼀个⾮常流⾏的设计模式(代码套路)

  • 核⼼: 将元素定位及操作和业务逻辑, 拆分三个层⾯(每个层⾯对应⼀个单独的类), 然后通过调⽤完成最终的测试执⾏⾏为的过程

  • 三个层⾯: 对象库层/操作层/业务层

在这里插入图片描述

6. v4 版本

6.1 PO⻚⾯元素封装步骤

    1. 对应⻚⾯创建⻚⾯ PO 代码⽂件, 命名规则: ⻚⾯功能_page.py, 例如⾸⻚: index_page.py
    1. 定义三个类: 对象层(XxxPage)/操作层(XxxHandle)/业务层(XxxTask)
    1. 对象层:
    • 1> init ⽅法中获取浏览器对象
    • 2> ⾃定义⽅法: 封装元素定位⽅法
    • 3> 封装元素定位⽅法需要添加返回值!
    1. 操作层:
    • 1> init ⽅法获取对象层对象, 根据类名写对象变量名
    • 2> ⾃定义⽅法: 封装元素操作⽅法
    1. 业务层:
    • 1> init ⽅法获取操作层对象, 根据类名写对象变量名
    • 2> ⾃定义⽅法: 封装测试业务逻辑
    1. 在测试⽤例⽂件中, 实例化业务层对象, 调⽤测试业务⽅法, 执⾏测试

代码示例: ⾸⻚⻚⾯

"""
⾸⻚⻚⾯
"""
from utils import DriverUtilclass IndexPage(object):"""⾸⻚对象层"""def __init__(self):self.driver = DriverUtil.get_driver() # 获取浏览器对象def find_login(self):"""定位登录⽅法"""# self.driver.find_element_by_link_text('登录')# self.driver = DriverUtil.get_driver() # 获取浏览器对象return self.driver.find_element_by_link_text('登录')class IndexHandle(object):"""⾸⻚操作层"""def __init__(self):self.index_page = IndexPage() # 获取对象层对象def click_login(self):"""点击登录⽅法"""# element = IndexPage()# element.find_login().click()self.index_page.find_login().click()class IndexTask(object):"""⾸⻚业务层"""def __init__(self):self.index_handle = IndexHandle() # 获取操作层对象def go_to_login(self):"""跳转登录⻚⾯⽅法"""self.index_handle.click_login()

代码示例: 登录⻚⾯

"""
登录⻚⾯
"""
from utils import DriverUtilclass LoginPage(object):"""登录对象层"""def __init__(self):self.driver = DriverUtil.get_driver() # 获取浏览器对象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')class LoginHandle(object):"""登录操作层"""def __init__(self):self.login_page = LoginPage() # 获取对象层对象def input_username(self, name):"""输⼊⽤户名⽅法"""self.login_page.find_username().send_keys(name)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()class LoginTask(object):"""登录业务层"""def __init__(self):self.login_handle = LoginHandle() # 获取操作层对象def login_method(self, name, pwd, code):"""登录⽅法"""self.login_handle.input_username(name) # 输⼊⽤户名self.login_handle.input_password(pwd) # 输⼊密码self.login_handle.input_verify_code(code) # 输⼊验证码self.login_handle.click_login_btn() # 点击登录按钮

6.2 测试⽤例的最终代码样式

"""
整合多个脚本⾄同⼀个测试⽤例中
"""
import pytest
from utils import DriverUtil, get_alert_msg
from v4.index_page import IndexTask
from v4.login_page import LoginTaskclass TestLogin(object):"""登录测试类"""def setup_class(self):self.driver = DriverUtil.get_driver() # 获取浏览器对象self.index_task = IndexTask() # 实例化⾸⻚业务层对象self.login_task = LoginTask() # 实例化登录⻚⾯业务层对象def teardown_class(self):DriverUtil.quit_driver() # 退出浏览器对象def setup(self):# 打开⾸⻚self.driver.get('http://127.0.0.1/')# 点击登录# self.driver.find_element_by_link_text('登录').click()self.index_task.go_to_login() # 跳转登录def teardown(self):passdef test_account_does_not_exist(self):"""账号不存在测试⽅法"""# # 2. 输⼊⼀个不存在的⽤户名# 
self.driver.find_element_by_id('username').send_keys('13811110001')# # 3. 输⼊密码# 
self.driver.find_element_by_id('password').send_keys('123456')# # 4. 输⼊验证码# 
self.driver.find_element_by_id('verify_code').send_keys('8888')# # 5. 点击登录按钮# self.driver.find_element_by_name('sbtbutton').click()self.login_task.login_method('13811110001', '123456', '8888')# 6. 获取错误提示信息msg = get_alert_msg() # 获取弹窗信息print('错误信息为:', msg)def test_wrong_password(self):"""密码错误测试⽅法"""# # 2. 输⼊⽤户名# 
self.driver.find_element_by_id('username').send_keys('13800001111')# # 3. 输⼊错误密码#self.driver.find_element_by_id('password').send_keys('error')# # 4. 输⼊验证码# 
self.driver.find_element_by_id('verify_code').send_keys('8888')# # 5. 点击登录按钮# self.driver.find_element_by_name('sbtbutton').click()self.login_task.login_method('13800001111', 'error', '8888')# 6. 获取错误提示信息msg = get_alert_msg() # 获取弹窗信息print('错误信息为:', msg)if __name__ == '__main__':pytest.main(['-s', 'tpshop_login.py'])

7. v5 版本

7.1 PO ⽂件对象层代码优化

class LoginPage(object):"""登录对象层"""def __init__(self):self.driver = DriverUtil.get_driver() # 获取浏览器对象# 说明: 将元素的定位⽅法及特征值封装成属性, 能够实现集中管理⽬标元素的定位⽅法及特征值self.name = (By.ID, 'username') # ⽤户名self.pwd = (By.ID, 'password') # 密码self.code = (By.ID, 'verify_code') # 验证码self.btn = (By.NAME, 'sbtbutton') # 登录按钮def find_username(self):"""定位⽤户名⽅法"""# return self.driver.find_element_by_id('username')# return self.driver.find_element(By.ID, 'username')return self.driver.find_element(self.name[0], self.name[1])def find_password(self):"""定位密码⽅法"""# return self.driver.find_element_by_id('password')return self.driver.find_element(self.pwd[0], self.pwd[1])def find_verify_code(self):"""定位验证码⽅法"""# return self.driver.find_element_by_id('verify_code')return self.driver.find_element(self.code[0], self.code[1])def find_login_btn(self):"""定位登录按钮⽅法"""# return self.driver.find_element_by_name('sbtbutton')return self.driver.find_element(self.btn[0], self.btn[1])

7.2 PO ⽂件操作层代码优化

class LoginHandle(object):"""登录操作层"""def __init__(self):self.login_page = LoginPage() # 获取对象层对象def input_username(self, name):"""输⼊⽤户名⽅法"""# 说明: 在执⾏输⼊操作前, 应该先执⾏清空操作self.login_page.find_username().clear()self.login_page.find_username().send_keys(name)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()

相关文章:

  • micro-app前端微服务原理解析
  • 论文笔记(八十二)Transformers without Normalization
  • linux系统加固
  • 逻辑回归之参数选择:从理论到实践
  • GNU gettext 快速上手
  • 两向量平行公式、向量与平面平行公式、两平面平行公式;两向量垂直公式、向量与平面垂直公式、两平面垂直公式
  • 基于 HT 构建 2D 智慧仓储可视化系统的技术解析
  • IP 地址和 MAC 地址是如何转换的
  • mac下载homebrew 安装和使用git
  • 3.2goweb框架GORM
  • 基于BM1684X+RK3588的智能工业视觉边缘计算盒子解决方案
  • c++线程的创建
  • 【Hive入门】Hive高级特性:事务表与ACID特性详解
  • 大数据平台与数据仓库的核心差异是什么?
  • openharmony 4.1 运行busybox工具包(保姆教程)
  • encodeURI和encodeURICompoent的区别
  • 云备份服务器,数据备份服务器的方法有哪些?
  • 配置和使用基本存储
  • Golang - 实现文件管理服务器
  • 如何用AI生成个人职业照/西装照?
  • 关于“十五五”,在上海召开的这场座谈会释放最新信号
  • 中办、国办印发《安全生产考核巡查办法》
  • 癌症来临前,可能伪装成这几种常见病,千万别大意
  • 言短意长|政府食堂、停车场开放的示范效应
  • 打造全域消费场景,上海大世界百个演艺娱乐新物种待孵化
  • 长三角铁路“五一”假期运输今启动:预计发送旅客量增6%,5月1日当天有望创新高