遵循面对对象设计模式
base.py
from selenium import webdriver
from plugins import Single, read_yaml
class BasePage:
def __init__(self):
# 确保浏览器同一时间只开启一个
self._driver = WebdriverCreator().get_browser()
# 根据继承树读取元素配置信息--排除BasePage和object
cls_list = [cls.__name__ for cls in self.__class__.mro()][:-2]
print(cls_list)
for cls in cls_list:
locators = read_yaml('conf/ui_locators.yml')[cls]
for k, v in locators.items():
setattr(self, k, v) # 批量赋值
def click(self, locator): # locator ['id','#email']
self._driver.find_element(*locator).click()
def input_text(self, locator, text):
self._driver.find_element(*locator).send_keys(text)
# 返回元素文本
def ele_text(self, locator):
return self._driver.find_element(*locator).text
# 点击多个元素
def click_multi(self, locators):
eles = self._driver.find_elements(*locators)
for ele in eles:
ele.click()
# 根据目标元素文本点击
def click_by_text(self, locators, target_text):
# 筛选出一堆元素
eles = self._driver.find_elements(*locators)
# 遍历元素列表判断元素的文本是否等于target_text
for ele in eles:
if ele.text == target_text:
ele.click()
break # 点击一个就退出
# 获取多个元素文本列表
def eles_text(self, locators):
eles = self._driver.find_elements(*locators)
return [ele.text for ele in eles]
def close_browser(self):
self._driver.quit()
# 当元素存在就点击
def click_ele_exist(self, *locator):
btns = self.driver.find_elements(*locator)
# 假如出现用户协议弹出按钮
if btns:
btns[0].click()
# 输入文本
def input_text(self, text, *locator):
self.driver.find_element(*locator).send_keys(text)
# js方式点击元素
def click_by_js(self, css):
self.driver.execute_script(f"document.querySelector('{css}').click()")
# 切换目标窗口
def switch_window(self, target_title):
for handle in self.driver.window_handles:
self.driver.switch_to.window(handle)
# 判断切换到目标窗口-判断当前窗口标题是否为:小米10系列立即购买-小米商城
if target_title == self.driver.title:
print('切换到目标窗口')
# 进入到商品详情页
return True
# 获取目标元素文本
def get_text(self, *locator):
return self.driver.find_element(*locator).text
class WebdriverCreator(Single):
def get_browser(self): # 获取浏览器
if not hasattr(self, '_driver'):
self._driver = webdriver.Chrome() # 会打开浏览器
self._driver.implicitly_wait(10)
# 设置页面加载超时时间
self._driver.set_page_load_timeout(60)
# 设置js执行超时时间
self._driver.set_script_timeout(60)
return self._driver
其他工具类
plugins.py
import functools
import os
from logging.handlers import RotatingFileHandler
from logging import Formatter
import logging
def get_logger(name,out_level,file_name):
LOG_FORMAT = '%(asctime)s-%(levelname)s-%(message)s' #日志格式
DATA_FORMAT = '%m/%d/%Y %H:%M:%S %p' # 时间格式化 需要日志格式化中定义%(asctime)s
# logger日志器同名时为单例模式
logger = logging.getLogger(name) # 获取日志器
logger.setLevel(level=out_level) #设置输出级别为debug
# 日志模块的高级用法
# 1.需要有个日志处理器---控制输出方向--(可以输出到文本,可以输出到控制台)
log_handler = RotatingFileHandler(file_name,encoding='utf-8',maxBytes=1024*20) #文件名,编码,最大字节数20kb,
# 2.需要给日志器配置输出格式,需要有个格式化对象
formatter = Formatter(fmt=LOG_FORMAT,datefmt=DATA_FORMAT)
log_handler.setFormatter(formatter) # 给日志处理器配置输出格式
# 3.装配日志处理器
if logger.handlers == []: # 如果没有日志器再装配
logger.addHandler(log_handler)
return logger
# 封装日志装饰器,提供以下功能
# 1.可以自定义日志级别(定义输出和输出级别)
# 2.可以自动写日志
def logged(in_level=10,out_level=10,msg=None): #日志输入输出级别
def decorate(func):
@functools.wraps(func)
def inner(*args,**kwargs):
# 判断是否由logs目录,没有就自动创建
if not os.path.exists('logs'):
os.mkdir('logs')
# 记录日志的地方
logger = get_logger('haiwen',out_level,'logs/testdemo.log')
# 如果输入级别小于输出级别--不会输出数据--可以给出警告
if in_level < out_level:
logger.warning('输入级别小于输出级别')
# 根据msg判断是否要自动写日志
log_msg=''
if msg:
#使用传递的参数作为日志信息
log_msg=msg
else:
# 自动写日志信息--xxx函数 执行了
log_msg=f'{func.__name__} 用例 执f行了'
# 用例运行
try:
res = func(*args,**kwargs)
logger.log(level=in_level,msg=log_msg) #记录正常信息
return res
except:
logger.error(msg='异常信息: ',exc_info=True) #记录异常信息-堆栈信息
return inner
return decorate
class Single:
# 产生对象的是new方法
def __new__(cls, *args, **kwargs):
# 判断当前类有没有对象,有就直接返回,没有就创建一个再返回
if not hasattr(cls,'_instance'):
cls._instance = object.__new__(cls) # 产生当前类的对象
return cls._instance
import yaml
def read_yaml(path):
# 读取文件
with open(path,encoding='utf8') as f:
content = f.read()
# 将文件内容转成python数据格式
return yaml.safe_load(content)



