web自动化针对PO模式进行二次封装之basepage
在PO模式当中,我们做到了页面对象与测试用例的分离,但在页面对象编写时,我们仍然还有优化的空间。页面对象有一些共同的基本操作,可以封装起来,并可以在基本操作当中加上日志和异常截图的处理。比如说我们在查找元素时,都需要等待,在PO模式当中,需要都写上等待和查找元素,那么就可以将其封装起来,包括其它的一些比如:文本获取、元素属性获取、鼠标操作、窗口切换、iframe切换、alert弹框关闭、文件上传、下拉框选择......
当脚本运行的过程中,出现了用例失败,我们希望可以通过日志和截图来分析失败的原因,selenium并没有主动生成日志和截图,所以需要在代码中加上,使用webdriver的save_screenshot方法来截图,日志和截图会记录自动化用例的执行过程,并且,在用例的任何一个步骤出现了异常都会记录异常信息并生成相应的截图。当然为了更美观地跟allure集成,自然也需要将以异常截图展示在allure报告中,以下就是basepage.py的部分内容:
import time
from datetime import datetime
import allure
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from Common.handle_logger import case_logger
from Common.constants import OUTPUTS_DIR
from Common.upload_file import upload class BasePage:
'''
BasePage类,针对PageObjects类的二次封装
''' def __init__(self, driver: WebDriver):
self.driver = driver def wait_element_to_be_visible(self, loc, img_doc, timeout=20, frequency=0.5):
'''
等待元素可见
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("开始等待页面元素<{}>是否可见!".format(loc))
start_time = time.time()
WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
except Exception as e:
case_logger.error("页面元素<{}>等待可见失败!".format(loc))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("页面元素<{}>等待可见,等待时间:{}秒".format(loc, round(end_time - start_time, 2))) def wait_element_to_be_click(self, loc, img_doc, timeout=20, frequency=0.5):
'''
等待元素可点击
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("开始等待页面元素<{}>是否可点击!".format(loc))
start_time = time.time()
WebDriverWait(self.driver, timeout, frequency).until(EC.element_to_be_clickable(loc))
except Exception as e:
case_logger.error("页面元素<{}>等待可点击失败!".format(loc))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("页面元素<{}>等待可点击,等待时间:{}秒".format(loc, round(end_time - start_time, 2))) def wait_element_to_be_exist(self, loc, img_doc, timeout=20, frequency=0.5):
'''
等待元素存在
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("开始等待页面元素<{}>是否存在!".format(loc))
start_time = time.time()
WebDriverWait(self.driver, timeout, frequency).until(EC.presence_of_element_located(loc))
except Exception as e:
case_logger.error("页面元素<{}>等待存在失败!".format(loc))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("页面元素<{}>等待存在,等待时间:{}秒".format(loc, round(end_time - start_time, 2))) def save_screenshot(self, img_doc):
'''
页面截屏保存截图
:param img_doc: 截图说明
:return:
'''
file_name = OUTPUTS_DIR + "\\{}_{}.png".format(datetime.strftime(datetime.now(), "%Y%m%d%H%M%S"), img_doc)
self.driver.save_screenshot(file_name)
with open(file_name, mode='rb') as f:
file = f.read()
allure.attach(file, img_doc, allure.attachment_type.PNG)
case_logger.info("页面截图文件保存在:{}".format(file_name)) def get_element(self, loc, img_doc):
'''
获取页面中的元素
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:return: WebElement对象
'''
case_logger.info("在{}中查找元素<{}>".format(img_doc, loc))
try:
ele = self.driver.find_element(*loc)
except Exception as e:
case_logger.error("在{}中查找元素<{}>失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
return ele def get_elements(self, loc, img_doc):
'''
获取页面中的所有元素
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:return: WebElement对象
'''
case_logger.info("在{}中查找所有元素<{}>".format(img_doc, loc))
try:
ele = self.driver.find_elements(*loc)
except Exception as e:
case_logger.error("在{}中查找所有元素<{}>失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
return ele def input_text(self, text, loc, img_doc, timeout=20, frequency=0.5):
'''
对输入框输入文本内容
:param text: 输入的文本内容
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中输入元素<{}>的内容为{}".format(img_doc, loc, text))
self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
self.get_element(loc, img_doc).send_keys(text)
except Exception as e:
case_logger.error("在元素<{}>中输入内容{}失败!".format(loc, text))
self.save_screenshot(img_doc)
raise e def clear_text(self, loc, img_doc, timeout=20, frequency=0.5):
'''
清除文本框的内容
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中清除元素<{}>的文本内容".format(img_doc, loc))
self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
self.get_element(loc, img_doc).clear()
except Exception as e:
case_logger.error("在{}中清除元素<{}>的文本内容失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e def click_button(self, loc, img_doc, timeout=20, frequency=0.5):
'''
点击按钮
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中点击元素<{}>".format(img_doc, loc))
self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
self.get_element(loc, img_doc).click()
except Exception as e:
case_logger.error("在{}中点击元素<{}>失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e def get_element_text(self, loc, img_doc, timeout=20, frequency=0.5):
'''
获取WebElement对象的文本值
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return: WebElement对象的文本值
'''
try:
case_logger.info("在{}中获取元素<{}>的文本值".format(img_doc, loc))
self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
text = self.get_element(loc, img_doc).text
except Exception as e:
case_logger.error("在{}中获取元素<{}>的文本值失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
case_logger.info("获取到的元素文本值为:{}".format(text))
return text def get_elements_text(self, loc, img_doc, timeout=20, frequency=0.5):
'''
获取WebElement对象的所有文本值
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return: WebElement对象的文本值的列表
'''
try:
case_logger.info("在{}中获取元素<{}>的所有文本值".format(img_doc, loc))
self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
all_text = self.get_elements(loc, img_doc)
text_list = []
for one_text in all_text:
text_list.append(one_text.text)
except Exception as e:
case_logger.error("在{}中获取元素<{}>的所有文本值失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
case_logger.info("获取到的元素文本值列表为:{}".format(text_list))
return text_list def get_element_attr(self, attr_name, loc, img_doc, timeout=20, frequency=0.5):
'''
获取WebElement对象的属性值
:param attr_name: 属性名称
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return: WebElement对象的属性值
'''
try:
case_logger.info("在{}中获取元素<{}>的属性{}的值".format(img_doc, loc, attr_name))
self.wait_element_to_be_exist(loc, img_doc, timeout, frequency)
value = self.get_element(loc, img_doc).get_attribute(attr_name)
except Exception as e:
case_logger.error("在{}中获取元素<{}>的属性{}的值失败!".format(img_doc, loc, attr_name))
self.save_screenshot(img_doc)
raise e
else:
case_logger.info("获取到的元素属性{}的值为{}".format(attr_name, value))
return value def switch_to_frame(self, loc, img_doc, timeout=20, frequency=0.5):
'''
切换iframe页面
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中根据元素<{}>进行iframe切换".format(img_doc, loc))
start_time = time.time()
WebDriverWait(self.driver, timeout, frequency).until(EC.frame_to_be_available_and_switch_to_it(loc))
except Exception as e:
case_logger.error("在{}中根据元素<{}>进行iframe切换失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("在{}中根据元素<{}>进行iframe切换,等待时间:{}秒".
format(img_doc, loc, round(end_time - start_time, 2))) def switch_to_default_content(self, img_doc):
'''
切换iframe到main页面
:param img_doc: 截图说明
:return:
'''
try:
case_logger.info("切换iframe到main页面")
self.driver.switch_to.default_content()
except Exception as e:
case_logger.error("切换iframe到main页面失败!")
self.save_screenshot(img_doc)
raise e def switch_to_parent(self, img_doc):
'''
切换iframe到上一层页面
:param img_doc: 截图说明
:return:
'''
try:
case_logger.info("切换iframe到上一层页面")
self.driver.switch_to.parent_frame()
except Exception as e:
case_logger.error("切换iframe到上一层页面失败!")
self.save_screenshot(img_doc)
raise e def upload_file(self, filename, img_doc, browser_type="chrome"):
'''
非input标签的文件上传
:param filename: 文件名(绝对路径)
:param img_doc: 截图说明
:param browser_type: 浏览器类型
:return:
'''
try:
case_logger.info("上传文件({})".format(filename))
time.sleep(2)
upload(filePath=filename, browser_type=browser_type)
except Exception as e:
case_logger.error("上传文件({})失败!".format(filename))
self.save_screenshot(img_doc)
raise e
else:
time.sleep(2) def suspend_mouse(self, loc, img_doc, timeout=20, frequency=0.5):
'''
鼠标悬浮
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}上根据元素<{}>进行悬浮".format(img_doc, loc))
self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
ele = self.get_element(loc, img_doc)
ActionChains(self.driver).move_to_element(ele).perform()
except Exception as e:
case_logger.error("在{}上根据元素<{}>进行悬浮失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e def alert_close(self, img_doc, alert_type='alert', text=None, timeout=20, frequency=0.5):
'''
弹框关闭
:param img_doc: 截图说明
:param alert_type: 弹框类型:alert/confirm/prompt
:param text: prompt弹框输入的文本
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中切换并关闭{}类型的弹框".format(img_doc, alert_type))
start_time = time.time()
WebDriverWait(self.driver, timeout, frequency).until(EC.alert_is_present())
if alert_type in ['alert', 'confirm']:
self.driver.switch_to.alert.accept()
elif alert_type == 'prompt':
self.driver.switch_to.alert.send_keys(text)
self.driver.switch_to.alert.accept()
else:
case_logger.error("不支持{},请确认alert的类型".format(alert_type))
except Exception as e:
case_logger.error("在{}中切换并关闭{}类型的弹框失败!".format(img_doc, alert_type))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("在{}中切换并关闭{}类型的弹框,等待时间:{}秒".
format(img_doc, alert_type, round(end_time - start_time, 2))) def select_action(self, loc, img_doc, content, select_type, timeout=20, frequency=0.5):
'''
Select操作
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param content: select_by_方法的入参
:param select_type: select类型
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}上根据元素<{}>以{}方式进行下拉选择".format(img_doc, loc, select_type))
self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
ele = self.get_element(loc, img_doc)
if select_type == 'index':
Select(ele).select_by_index(content)
elif select_type == 'value':
Select(ele).select_by_value(content)
elif select_type == 'text':
Select(ele).select_by_visible_text(content)
else:
case_logger.error("不支持{},请确认Select的类型".format(select_type))
except Exception as e:
case_logger.error("在{}上根据元素<{}>以{}方式进行下拉选择失败!".format(img_doc, loc, select_type))
self.save_screenshot(img_doc)
raise e def switch_to_windows(self, loc, img_doc, timeout=20, frequency=0.5):
'''
窗口切换
:param loc: 元素定位的XPATH元组表达式
:param img_doc: 截图说明
:param timeout: 等待的超时时间
:param frequency: 轮询频率
:return:
'''
try:
case_logger.info("在{}中根据元素<{}>进行窗口切换".format(img_doc, loc))
cur_handles = self.driver.window_handles # 获取点击之前的窗口总数
start_time = time.time()
self.click_button(loc, img_doc, timeout, frequency) # 点击按钮后新的窗口出现
WebDriverWait(self.driver, timeout, frequency).until(EC.new_window_is_opened(cur_handles))
wins = self.driver.window_handles # 再次获取窗口总数
self.driver.switch_to.window(wins[-1]) # 切换到新的页面
except Exception as e:
case_logger.error("在{}中根据元素<{}>进行窗口切换失败!".format(img_doc, loc))
self.save_screenshot(img_doc)
raise e
else:
end_time = time.time()
case_logger.info("在{}中根据元素<{}>进行窗口切换,等待时间:{}秒".
format(img_doc, loc, round(end_time - start_time, 2)))
basepage模块在web自动化里面起到了至关重要的作用,它起到了优化代码,多次调用,更高的稳定性,日志记录,异常截图等优点,在PageObjects里面直接继承它用来封装页面的各种行为!
web自动化针对PO模式进行二次封装之basepage的更多相关文章
- APP自动化针对PO模式进行二次封装之basepage
APP自动化跟WEB自动化所使用的框架基本一样,都是采用的PO模式结合pytest框架编写自动化测试脚本,为了提高代码的复用性.稳定性和易维护性,我们针对PO模式进行了二次封装,将日志,等待以及异常截 ...
- 【Selenium07篇】python+selenium实现Web自动化:PO模型,PageObject模式!
一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第七篇博 ...
- 【python+selenium的web自动化】- PageObject模式解析及案例
如果想从头学起selenium,可以去看看这个系列的文章哦! https://www.cnblogs.com/miki-peng/category/1942527.html PO模式 Page O ...
- Web自动化测试框架-PO模式
Web自动化测试框架(WebTestFramework)是基于Selenium框架且采用PageObject设计模式进行二次开发形成的框架. 一.适用范围:传统Web功能自动化测试.H5功能自动化测试 ...
- 【Selenium01篇】python+selenium实现Web自动化:搭建环境,Selenium原理,定位元素以及浏览器常规操作!
一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 二.话不多说,直接开干,开始搭建自动化测试环境 这里以前在 ...
- 自动化测试po模式是什么?自动化测试po分层如何实现?-附详细源码
一.什么是PO模式 全称:page object model 简称:POM/PO PO模式最核心的思想是分层,实现松耦合!实现脚本重复使用,实现脚本易维护性! 主要分三层: 1.基础层BaseP ...
- python selenium - web自动化环境搭建
前提: 安装python环境. 参考另一篇博文:https://www.cnblogs.com/Simple-Small/p/9179061.html web自动化:实现代码驱动浏览器进行点点点的操作 ...
- 【Selenium05篇】python+selenium实现Web自动化:读取ini配置文件,元素封装,代码封装,异常处理,兼容多浏览器执行
一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第五篇博 ...
- 【Selenium06篇】python+selenium实现Web自动化:日志处理
一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第六篇博 ...
随机推荐
- Go语言系列:(2)go get 命令介绍
Go语言的代码被托管于 Github.com 网站,该网站是基于 Git 代码管理工具的,很多有名的项目都在该网站托管代码.其他类似的托管网站还有 code.google.com.bitbucket. ...
- [Go]TCP服务中增加消息队列与工作池
之前的处理中每一个连接都会创建一个主groutine , 每个连接中的主groutine中创建出读groutine 和写groutine 每个连接处理业务再单独开出一个groutine ,这样如果有1 ...
- ORM映射(对象关系映射)
ORM映射(对象关系映射)分创建表和操作表两个部分创建单表创建关联表(foreignKey) 一对一 一对多(重点) 多对多(重点) 创建表后加str方法把打印的地址转换成对应字符表的操作(增删改查) ...
- dp习题
仅收录有意思的题目 数的划分 导弹拦截 : LIS的两种优化 教主的花园:将不同的情况分类,最后取max 午餐 & 挂饰: 需要排序 挂饰:0-1背包处理负数体积 投资的最大效益 : 完全背包 ...
- JavaScript-----9.函数
1.函数的使用 1.1 声明函数和调用函数 //1.声明函数 //function 函数名() { // //函数体 //} function sayHi() { console.log('hi~') ...
- 2019-2020-1 20199305《Linux内核原理与分析》第十二周作业
缓冲区溢出漏洞实验 (一)何为缓冲区溢出漏洞 缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况.这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段.这一漏洞的出现是由于 ...
- 【洛谷5644】[PKUWC2018] 猎人杀(容斥+生成函数+分治NTT)
点此看题面 大致题意: 有\(n\)个人相互开枪,每个人有一个仇恨度\(a_i\),每个人死后会开枪再打死另一个还活着的人,且第一枪由你打响.设当前剩余人仇恨度总和为\(k\),则每个人被打中的概率为 ...
- PHP如何开启swoole扩展
swoole是一个PHP的异步.并行.高性能网络通信引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库连接池,AsyncT ...
- 在ASP.NET中备份数据库以及还原(不成熟)
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- CSS权重的进制问题
这是复习篇的第一个知识点,(CSS权重进制在IE6为256,后来扩大到了65536.而现代浏览器则采用更大的数量)在说这个知识点之前我们先来看一个例子 <!DOCTYPE html> &l ...