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

PO设计模式详解(Python+selenium+unittest)

一、什么是PO设计模式(Page Object Model)

1、Page Object是一种设计模式,它主要体现在对界面交互细节的封装上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性。

2、一般PO设计模式有三层

第一层:

  • 对Selenium 进行二次封装,定义一个所有页面都继承的 BasePage ,
  • 封装 Selenium 基本方法 例如:元素定位,元素等待,导航页面 ,
  • 不需要全部封装,用到多少方法就封装多少方法。

第二层:

  • 页面元素进行分离,每个元素只定位一次,隔离定位,如果页面改变,只需要改变相应的元素定位;
  • 业务逻辑分离 或 操作元素动作分离

第三层:

使用单元测试框架对业务逻辑进行测试

同时,我也准备了一份软件测试视频教程(含接口、自动化、性能等),需要的可以直接在下方观看,或者直接关注VX公众号:互联网杂货铺,免费领取

软件测试视频教程观看处:

2023完整版阿里大牛7天软件测试零基础速成内部教程,从基础到项目实战学完即入职。

二、为什么要使用PO设计模式

  • 页面频繁变化,(页面html结构等变化)导致页面UI元素频繁变动,元素定位改变
  • 传统线性自动化(面向过程开发),用例中需要反复的定位同一个元素
  • 每当页面发生变化的时候,需要在用例中寻找变动的部分,工作量大,容易产生遗漏,不容易维护

三、PO设计模式的六大原则

  • 公共方法代表页面提供的服务
  • 不要暴露细节
  • 不要在封装的框架中做断言
  • 方法可以return到新打开的页面
  • 不要对所有元素建模,仅对自己关注的元素建模
  • 相同的行为会产生不同的结果,可以封装不同的结果

四、PO设计模式实例

以公司的统一登录作为项目例子,用PO设计模式实现登陆:

1、手工用例

2、用PO模式实现自动化用例

项目目录

Base.py

login_page.py

from Page import Base# 创建LoginPage类继续BasePage类
class LoginPage(Base.BasePage):'''统一平台登录Page层,登录页面封装操作到的元素''''''第二层:页面元素进行分离,每个元素只定位一次,操作元素动作分离'''# 定义url变量,供父类中的open()方法使用url ="https://test01....cn/#/login"# 用户名输入框定位def form_username(self,user_name):return self.by_id("name").send_keys(user_name) # 使用了父类的self.by_id()方法定位元素,简洁了不少# 密码输入框定位def form_password(self,pass_word):return self.by_id("password").send_keys(pass_word)# 登录按钮定位def button_login(self):return self.by_xpath("//*[text()='登录']").click()

test_login.py

from Page import login_page
import unittest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.keys import Keys
from CommonMethod import LogUtillogger = LogUtil.logs() # 调用封装的日志方法,将日志输出到控制台以及写入文件class LoginCase(unittest.TestCase):'''第三层:用单元测试框架对业务逻辑进行测试''''''使用LoginPage类及它所继承的父类中的方法'''@classmethoddef setUpClass(cls):# 实例化webdriver,俗称:打开浏览器cls.driver = webdriver.Firefox(executable_path='E:\\UI test\\UnittestProject\\Driver\\geckodriver.exe')cls.driver.implicitly_wait(10)@classmethoddef tearDownClass(cls):cls.driver.quit()def test_login_success(self):page = login_page.LoginPage(self.driver) # 需要用到哪个Page类时,只需要将它传入浏览器驱动,就可以使用该类中提供的方法了page.open()page.form_username("XXX")page.form_password("123456")page.button_login()sleep(2)self.assertEqual(page.get_current_url(), "https://test01....cn/#/home")print("登录成功,用例执行结果通过,当前的url为"+ page.get_current_url())sleep(1)def test_login_fail(self):page = login_page.LoginPage(self.driver)page.open()page.form_username("XXX11")page.form_password("123456")page.button_login()self.assertNotEqual(page.get_current_url(), "https://test01....cn/#/home")print("登录失败,用例执行结果通过,当前的url为"+ page.get_current_url())page.form_username(Keys.CONTROL+'a') # 输入组合键Ctrl+a,全选输入框内容page.form_username(Keys.BACK_SPACE) # 删除键,删除选中的内容page.form_password(Keys.CONTROL + 'a')page.form_password(Keys.BACK_SPACE)sleep(1)
if __name__ == '__main__':unittest.main(verbosity=2)

执行结果

在test_login.py中有调用封装的日志方法,这里把封装的日志附上,在CommonMethod目录下的LogUtil.py

五、其他补充

1、相同的行为会产生不同的结果,可以封装不同的结果:在login_page针对【登录】按钮封装了2个方法

2、方法可以return到新打开的页面:在login_page针对【登录】按钮封装,封装了之后要return新页面或其他信息。test_login调用时命名变量来接收这个函数就行了,比如indexurl = page.button_login_success(),在后面断言可以用indexurl变量来跟预期的url断言

 # 登录失败封装def button_login_fail(self):self.by_xpath("//span[text()='登录']").click()toast = self.by_xpath("//p[text()='账号或密码错误!']").textreturn toast# 登录成功封装def button_login_success(self):self.by_xpath("//span[text()='登录']").click()sleep(2)windows = self.driver.window_handles# 获取打开的多个窗口句柄self.driver.switch_to.window(windows[-1])# 切换到当前最新打开的窗口indexurl = self.get_current_url()return indexurl

3、断言:可以通过url、页面标题、text来断言

'''断言跳转的地址,通过try except语句块来进行测试断言,在实际自动化测试脚本开发中,经常要用到处理异常'''try:self.assertEqual(indexurl,"https://qa-xxxt/add")print("点击创建,正确跳转到新页面" + indexurl)except AssertionError as msg:print("没有跳转到正确页面,当前跳转的地址为"+addurl+"\n报错信息如下"+format(msg))'''当断言失败时会抛出异常测试用例执行失败,输出提示信息后重新将异常抛出,即raise,若不重新抛出,用例则永远是显示执行成功的,因为它把异常处理掉了'''raise msg
  try:self.assertEqual(toast, "账号或密码错误!")print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)except AssertionError as msg:print("错误提示语与预期结果不一致,请检查"+ format(msg))raise msg
  try:self.assertEqual(toast, "账号或密码错误!")print("登录失败用例场景执行通过,正确弹出提示信息为:" + toast)except AssertionError as msg:print("错误提示语与预期结果不一致,请检查"+ format(msg))raise msg

六、写在最后

PS:这套软件测试的自学教程合集。对于在测试行业发展的小伙伴们来说应该会很有帮助。全套内容已经打包到网盘,内容总量接近500个G。如需要软件测试学习资料,关注公众号(互联网杂货铺),后台回复1,整理不易,给个关注点个赞吧,谢谢各位大佬!

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

相关文章:

  • graylog日志分析配置(1)
  • 接口自动化测试操作流程
  • OpenCV中更稳更快的边缘检测方法,快速查找线、圆、椭圆--EdgeDrawing-C++代码
  • 【深度学习环境】windows安装 NVIDIA Docker
  • 【python】9个python进阶技巧(实用)
  • Outlook如何删除邮箱账户
  • 石英增强光声光谱气体传感技术中的高精密压力控制解决方案
  • Redis学习笔记10:基于spring的Lettuce redis客户端Pipelining管道
  • centos7 yum安装python3.9时报错【没有可用软件包 python3.9。 错误:无须任何处理】
  • 开放领域问答机器人2——开发流程和方案
  • 【避雷选刊】Springer旗下2/3区,2个月录用!发文量激增,还能投吗?
  • 使用matlab实现图像信号的色彩空间转换
  • HslCommunication模拟西门子读写数据
  • 压测必经之路,Jmeter分布式压测教程!
  • 响应式摄影科技传媒网站模板源码带后台
  • 〔开发系列〕一次关于小程序开发的深度总结
  • 0x05 Python数据分析,Anaconda八斩刀
  • AngularJS指令开发(1)——参数详解
  • crontab执行失败的多种原因
  • CSS 三角实现
  • es6(二):字符串的扩展
  • ES6系统学习----从Apollo Client看解构赋值
  • hadoop入门学习教程--DKHadoop完整安装步骤
  • Javascript Math对象和Date对象常用方法详解
  • JavaScript 基本功--面试宝典
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • LintCode 31. partitionArray 数组划分
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • Lucene解析 - 基本概念
  • Sass Day-01
  • underscore源码剖析之整体架构
  • 读懂package.json -- 依赖管理
  • 对象引论
  • 力扣(LeetCode)22
  • 前端之React实战:创建跨平台的项目架构
  • 什么是Javascript函数节流?
  • 学习JavaScript数据结构与算法 — 树
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 怎么将电脑中的声音录制成WAV格式
  • ionic入门之数据绑定显示-1
  • 带你开发类似Pokemon Go的AR游戏
  • 容器镜像
  • 如何通过报表单元格右键控制报表跳转到不同链接地址 ...
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • (1)(1.13) SiK无线电高级配置(五)
  • (12)Hive调优——count distinct去重优化
  • (2.2w字)前端单元测试之Jest详解篇
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (六)vue-router+UI组件库
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (三)uboot源码分析
  • (十八)SpringBoot之发送QQ邮件
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (状压dp)uva 10817 Headmaster's Headache
  • ****** 二十三 ******、软设笔记【数据库】-数据操作-常用关系操作、关系运算