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

day16-测试自动化之selenium的PO模式

一、PO模式介绍       

        PO(Page Object)模式是一种在自动化测试中常用的设计模式,将页面的每个元素封装成一个对象,通过操作对象来进行页面的交互。

        一般分为六个版本,现在大部分企业都用的V4版本,三层结构(base+page+scripts)

        V1:不使用任何设计模式和单元测试框架

                问题:无法批量运行

                代码例子

# 导包
from time import sleep
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait# 获取浏览器对象
chromedriver_path = r"C:\Program Files\Google\Chrome\Application\chromedriver.exe"
service = Service(executable_path=chromedriver_path)
driver = webdriver.Chrome(service=service)def main():# 打开页面driver.get("http://www.tpshop.com/index.php")# 网页最大化driver.maximize_window()# 隐式等待driver.implicitly_wait(30)# 点击登录链接driver.find_element(By.CSS_SELECTOR,"body > div.tpshop-tm-hander > div.top-hander.clearfix > div > div > div.fl.nologin > a.red").click()# 输入用户名driver.find_element(By.CSS_SELECTOR,"#username").send_keys("admin")# 输入密码driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")# 输入验证码driver.find_element(By.CSS_SELECTOR,"#verify_code").send_keys("8888")# 点击登录按钮driver.find_element(By.CSS_SELECTOR,"#loginform > div > div.login_bnt > a").click()# 获取错误提示信息msg = driver.find_element(By.CSS_SELECTOR,".layui-layer-content").text# 断言# assert msg == "用户名不存在!"# assertNotIn()# 点击提示框确定按钮driver.find_element(By.CSS_SELECTOR,".layui-layer-btn0").click()# 暂停两秒钟sleep(2)# 关闭浏览器驱动driver.quit()if __name__ == '__main__':main()

        V2:使用UnitTeSt管理用例

                问题:业务脚本没有与页面对象分开

        V3:使用方法封装的思想,对代码进行优化

                问题:代码冗余量太大了

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

                结构:

                        base(基类):page页面一些公共的方法;

                                1.初始化方法

                                2.查找元素方法

                                3.点击元素方法

                                4.输入方法

                                5.获取文本方法

                                6.截图方法

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

                                注意:

                                1.以上方法封装时候,解包只需1此,在查找元素解包

                                2.driver为虚拟,谁调用base时,谁传入,无需关注从哪里来

                                3.loc:真正使用1oc的方法只有查找元素方法使用

                                代码                

import time
import page
from selenium.webdriver.support.wait import WebDriverWait
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()class Base:def __init__(self, driver):log.info("[base]: 正在获取初始化driver对象:{}".format(driver))self.driver = driver# 查找元素方法 封装def base_find(self, loc, timeout=30, poll=0.5):log.info("[base]: 正在定位:{} 元素,默认定位超时时间为: {}".format(loc, timeout))# 使用显示等待 查找元素return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll).until(lambda x: x.find_element(*loc))# 点击元素 方法封装def base_click(self, loc):log.info("[base]: 正在对:{} 元素实行点击事件".format(loc))self.base_find(loc).click()# self.driver.execute_script("arguments[0].click();", self.base_find(loc))# 输入元素 方法封装def base_input(self, loc, value):# 获取元素el = self.base_find(loc)# 清空log.info("[base]: 正在对:{} 元素实行清空".format(loc))el.clear()# 输入el.send_keys(value)# 获取文本信息 方法封装def base_get_text(self, loc):log.info("[base]: 正在获取:{} 元素文本值".format(loc))return self.base_find(loc).text# 截图 方法封装def base_get_image(self):log.info("[base]: 断言出错,调用截图")self.driver.get_screenshot_as_file("./image/{}.png".format(time.strftime("%Y_%m_%d %H_%M_%S")))# 判断元素是否存在 方法封装def base_element_is_exist(self, loc):try:self.base_find(loc, timeout=5)log.info("[base]: {} 元素查找成功,存在页面".format(loc))return True  # 代表元素存在except Exception as e:log.error("[base]:发生错误{},{} 元素查找失败,不存在当前页面".format(e, loc))return False  # 代表元素不存在# 回到首(页购物车、下订单、支付)都需要用到此方法def base_index(self):time.sleep(5)self.driver.get(page.URL)# 切换frame表单方法def base_switch_frame(self, name):self.driver.switch_to.frame(name)# 回到默认目录方法def base_default_content(self):self.driver.switch_to.default_content()# 切换窗口 方法 调用此方法def base_switch_to_window(self, title):log.info("正在执行切换title值为:{}窗口 ".format(title))self.driver.switch_to.window(self.base_get_title_handle(title))# 获取指定title页面的handle方法def base_get_title_handle(self, title):# 获取当前页面所有的handlesfor handle in self.driver.window_handles:log.info("正在遍历handles:{}-->{}".format(handle, self.driver.window_handles))# 切换 handleself.driver.switch_to.window(handle)log.info("切换 :{} 窗口".format(handle))# 获取当前页面title 并判断 是否等于 指定参数titlelog.info("判断当前页面title:{} 是否等于指定的title:{}".format(self.driver.title, title))if self.driver.title == title:log.info("条件成立! 返回当前handle{}".format(handle))# 返回 handlereturn handle

                        page(页面对象):一个页面封装成一个对象;

                                应用:继承base;

                                实现:

                                        1.模块名:page+实际操作模块名称 如:page_login.py

                                        2.页面对象名:以大驼峰方法将模块名抄进来,有下划线去掉下划线

                                        3.方法:涉及元素,将每个元素操作单独封装一个操作方法

                                        4.组装:根据需求组装以上操作步骤

                                代码

from base.base import Base
import page
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()class PageLogin(Base):# 点击 登录链接def page_click_login_link(self):log.info("[page_loging] 执行:{} 点击链接操作".format(page.login_link))self.base_click(page.login_link)# 输入用户名def page_input_username(self, username):log.info("[page_loging] 对:{} 元素 输入用户名:{} 操作".format(page.login_username, username))self.base_input(page.login_username, username)# 输入密码def page_input_pwd(self, pwd):log.info("[page_loging] 对:{} 元素 输入密码:{} 操作".format(page.login_pwd, pwd))self.base_input(page.login_pwd, pwd)# 输入验证码def page_input_verify_code(self, verify_code):log.info("[page_loging] 对:{} 元素 输入验证码:{} 操作".format(page.login_verify_code, verify_code))self.base_input(page.login_verify_code, verify_code)# 点击登录按钮def page_click_login_btn(self):self.base_click(page.login_btn)# 获取 错误提示信息def page_get_error_info(self):return self.base_get_text(page.login_err_info)# 点击 错误提示框 确定按钮def page_click_error_alert(self):self.base_click(page.login_err_ok_btn)# 判断是否登录成功def page_if_login_success(self):# 注意 一定要将找元素的结果返回,True:存在return self.base_element_is_exist(page.login_logout_link)# 点击 安全退出def page_click_logout_link(self):self.base_click(page.login_logout_link)# 判断是否退出成功def page_if_logout_success(self):return self.base_element_is_exist(page.login_link)# 组合业务方法  -->登录业务直接调用def page_login(self, username, pwd, verify_code):log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))# 调用 输入用户名self.page_input_username(username)# 调用 输入密码self.page_input_pwd(pwd)# 调用 输入验证码self.page_input_verify_code(verify_code)# 调用 点击登录self.page_click_login_btn()# 组合登录业务方法 给(购物车模块、订单模块、支付模块)依赖登录使用def page_login_success(self, username="13812345678", pwd="123456", verify_code="8888"):# 点击登录连接self.page_click_login_link()log.info("[page_loging] 正在执行登录操作, 用户名:{} 密码:{}, 验证码:{}".format(username, pwd, verify_code))# 调用 输入用户名self.page_input_username(username)# 调用 输入密码self.page_input_pwd(pwd)# 调用 输入验证码self.page_input_verify_code(verify_code)# 调用 点击登录self.page_click_login_btn()

                        sripts/cases(业务层):导包调用page页面

                                实现

                                        1.模块:test+实际操作模块名称如:test_login.py

                                        2.测试业务名称:以大驼峰方法将模块名抄进来,有下划线去掉下划线

                                        3.方法

                                                1).初始化方法setup()注:在unittest框架中不能使用def__init_()方法

                                                        1.1).实例化页面对象

                                                        1.2).前置操作如:打开等等

                                                2).结束方法teardown

                                                        2.1).关闭驱动

                                                3).测试方法

                                                        3.1).根据要操作的业务实现

                                代码

import unittestfrom base.get_driver import GetDriver
from page.page_login import PageLogin
from parameterized import parameterized
from tool.read_txt import read_txt
from base.get_logger import GetLogger# 获取log日志器
log = GetLogger().get_logger()def get_data():arrs = []for data in read_txt("login.txt"):arrs.append(tuple(data.strip().split(",")))return arrs[1:]# 新建 登录测试类 并 继承 unittest.TestCase
class TestLogin(unittest.TestCase):# 新建 setupClass@classmethoddef setUpClass(cls):try:# 实例化 并获取drivercls.driver = GetDriver().get_driver()# 实例化 PageLogin()cls.login = PageLogin(cls.driver)# 点击登录连接cls.login.page_click_login_link()except Exception as e:log.error("错误:{}".format(e))# 截图cls.login.base_get_image()# 新建 tearDownClass@classmethoddef tearDownClass(cls):# 关闭drier驱动对象GetDriver().quit_driver()# 新建 登录测试方法@parameterized.expand(get_data())def test_login(self, username, pwd, verify_code, expect_result, status):try:# 调用 登录业务方法self.login.page_login(username, pwd, verify_code)# 判断是否为正向if status == "true":# 断言是否登录成功try:self.assertTrue(self.login.page_if_login_success())except Exception as e:# 截图self.login.base_get_image()log.error("错误:{}".format(e))raise  # 主动抛出异常,否则HTMLTestRunner无法显示该错误# 点击 安全退出self.login.page_click_logout_link()# 点击登录连接self.login.page_click_login_link()# 逆向用例else:# 获取错误提示信息msg = self.login.page_get_error_info()print("msg:", msg)try:self.assertEqual(msg, expect_result)except Exception as e:# 截图self.login.base_get_image()log.error("错误:{}".format(e))raise# 点击错误提示框 确定按钮self.login.page_click_error_alert()except Exception as e:log.error("错误:{}".format(e))# 截图self.login.base_get_image()raise

        V5:对PO分层之后的代码继续优化

        V6:PO模式深入封装,把共同操作提取封装到父类中,子类直接调用父类的方法                

二、今日学习思维导图

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • 八、MyBatis
  • 《网络编程实战系列》(17)网络桥接模式
  • 【设计模式】一文读懂策略模式
  • 【ML】Pre-trained Language Models及其各种微调模型的实现细节和特点
  • zip压缩包的格式不标准导致C++开源unzip.cpp解压失败问题的排查
  • loginApi
  • 每天五分钟深度学习pytorch:训练神经网络模型的基本步骤
  • 【竞品分析】竞品分析报告的基本模板
  • 裁剪或填充张量(Tensor)(四维与五维)(Python代码)
  • 【Python】requests的response.text 和 urllib.request 的 response.read()的区别
  • 编程-设计模式 26:业务代表模式
  • Android11 关于三方应用获取su权限
  • 以Zed项目为例学习大型Rust项目的组织与管理
  • 数据结构----二叉树
  • $translatePartialLoader加载失败及解决方式
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • 【node学习】协程
  • django开发-定时任务的使用
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • php面试题 汇集2
  • react 代码优化(一) ——事件处理
  • Swift 中的尾递归和蹦床
  • windows下mongoDB的环境配置
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 模型微调
  • 三栏布局总结
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 提醒我喝水chrome插件开发指南
  • 我是如何设计 Upload 上传组件的
  • 一道面试题引发的“血案”
  • # Panda3d 碰撞检测系统介绍
  • # 服务治理中间件详解:Spring Cloud与Dubbo
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #laravel 通过手动安装依赖PHPExcel#
  • #Lua:Lua调用C++生成的DLL库
  • #QT(一种朴素的计算器实现方法)
  • #宝哥教你#查看jquery绑定的事件函数
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (10)STL算法之搜索(二) 二分查找
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (c语言+数据结构链表)项目:贪吃蛇
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (java)关于Thread的挂起和恢复
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (每日持续更新)jdk api之StringBufferInputStream基础、应用、实战
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (一)SpringBoot3---尚硅谷总结
  • (转)EOS中账户、钱包和密钥的关系
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一