千纸鹤

  博客园  ::  :: 新随笔  ::  ::  :: 管理
  5 随笔 :: 70 文章 :: 0 评论 :: 9301 阅读
《-----关键字驱动加数据驱动:项目的目录结构-----》
(1)配置类:conf
chrome_options.py(chrome浏览器的配置 )
log.ini(日志的配置文件)
log_conf.py(生成日志器的配置)
(2)excel数据驱动类:excel_driver
excel_conf.py(Excel单元格写入时的样式定义)
excel_read.py(基于Excel文件的内容去进行读取)
(3)测试数据管理类:test_data
excel_driver_old.xlsx(弃用的测试用例)
mylog.log(异常的测试用例)
test.ddd.xlsx(正常的测试用例)
(4)关键字驱动类:web_keys
keys.py(封装关键字)
(5)框架的主入口
main.py

《一》配置类:conf
(1)chrome_options.py(chrome浏览器的配置 )
from time import sleep
from selenium import webdriver

class ChromeOptions:
def options(self):
# chrome浏览器的配置项,可以通过修改默认参数,改变默认启动的浏览器的形态
options = webdriver.ChromeOptions()
# 去掉默认的提示自动化信息:没啥用,一般没有什么影响。警告条可能会导致页面内容的遮挡或者挤压,影响自动化测试
options.add_experimental_option('excludeSwitches', ['enable-automation', 'enable-logging'])
# 去掉账号密码弹窗
prefs = {}
prefs["credentials-enable-service"] = False
prefs['profile.password_manager_enable'] = False
options.add_experimental_option("prefs", prefs)

return options
(2)log.ini(日志的配置文件)
[loggers] # 日志器
keys=root

[handlers] # 处理器(fh:文本处理器,sh:控制台处理器)
keys=fileHandler,streamHandler

[formatters] # 格式器
keys=simpleFormatter

[logger_root] # 设置日志器功能(设置级别,添加到文本和控制台)
level=INFO
handlers=fileHandler,streamHandler

[handler_fileHandler] # 设置文本处理器功能(创建控制台,设置级别,设置格式,存储位置)
class="FileHandler"
level=INFO
formatter=simpleFormatter
args=('mylog.log','a','utf-8')

[handler_streamHandler] # 设置控制台处理器功能(创建控制台,设置级别,设置格式)
class="StreamHandler"
level=INFO
formatter=simpleFormatter

[formatter_simpleFormatter] # 自定义的文件格式(日志的时间,当前执行程序名,日志的当前函数,日志级别名称,日志信息)
format=%(asctime)s %(filename)s %(levelname)s %(funcName)s %(message)s
(3)log_conf.py(生成日志器的配置)
import logging.config

# 路径一定要在调用的地方进行填写,不然会报错。
def get_log(path):
logging.config.fileConfig(path)
return logging.getLogger()

《二》excel数据驱动类:excel_driver
(1)excel_conf.py(Excel单元格写入时的样式定义)
from openpyxl.styles import PatternFill, Font

def write_result(cell, row, column, status=1): # (单元格、行、列、status参数)
# status:(1为Pass,其他为Failed)
if status == 1:
# 写入内容
cell(row=row, column=column).value = 'Pass'
# 单元格样式定义:绿色+加粗
cell(row=row, column=column).fill = PatternFill('solid', fgColor='AACF91')
cell(row=row, column=column).font = Font(bold=True)
else:
# 写入内容
cell(row=row, column=column).value = 'Failed'
# 单元格样式定义:红色+加粗
cell(row=row, column=column).fill = PatternFill('solid', fgColor='FF0000')
cell(row=row, column=column).font = Font(bold=True)
(2)excel_read.py(基于Excel文件的内容去进行读取)
 import openpyxl
from ui_demo.excel_driver import excel_conf
from ui_demo.web_keys.keys import Keys

# 读取excel中的用例正文
def run(file, log):
# 加载工作簿,返回一个excel对象,指定工作簿路径
excel = openpyxl.load_workbook(file)
try:
# 循环每个sheet页进行读取(sheet1、sheet2、sheet3...)
for name in excel.sheetnames:
# sheet(<Worksheet "Sheet1">、<Worksheet "Sheet2">...)
sheet = excel[name]
log.info('***********{}**************'.format(name))
# 读取到所有的表单的数据
for values in sheet.values:
# 如果第一个单元格是int类型,则表示进入了测试用例的正文
if type(values[0]) is int:
# 接收每一行操作行为对应的参数内容
data = {}
data['name'] = values[2]
data['value'] = values[3]
data['txt'] = values[4]
data['expect'] = values[6]
# 清除参数字典中为None的参数键值对
for key in list(data.keys()):
if data[key] is None:
del data[key]
# 基于操作行为和对应参数来执行自动化操作
'''
用例的操作行为主要分为:
1. 实例化,通过一个操作行为实例化关键字驱动类对象
2. 常规操作,通过调用已实例化的对象,执行对应的函数。
3. 断言操作,判断预期与实际是否符合,将结果填入测试用例中。
'''
# 运行期间的日志生成
log.info('当前正在执行:{}'.format(values[5]))
# 实例化关键字驱动
if values[1] == 'open_browser': # (调用open_browser函数方法:操作行为)
keys = Keys(data['txt'], log) # 等同于 Keys(txt='Chrome')(Chrome:参数)
# 断言操作
elif 'assert' in values[1]:# (调用assert函数方法:操作行为)
status = getattr(keys, values[1])(**data)
# 基于断言结果True or False来进行写入的操作
if status:
excel_conf.write_result(sheet.cell, row=values[0] + 2, column=8)
else:
excel_conf.write_result(sheet.cell, row=values[0] + 2, column=8, status=2)
# 断言后的excel写入
excel.save(file)
# 常规操作
else: # (调用除open_browser 和 assert以外的函数方法:操作行为)
getattr(keys, values[1])(**data)# 等同于 keys.input(name=xxx,value=xxx,txt=xxx)
except Exception as e:
log.exception('运行异常:{}'.format(e))
finally:
excel.close()

《三》测试数据管理类:test_data
(1)excel_driver.xlsx(正常的测试用例)(编号、操作行为、参数1、参数2、参数3、描述、预期结果、实际结果)
(2)excel_driver_old.xlsx(弃用的测试用例)
(3)mylog.log(异常的测试用例)(日志记录)
(4)test.ddd.xlsx(正常的测试用例)

《四》关键字驱动类:web_keys
(1)keys.py(封装关键字)
 '''
Selenium关键字驱动类:常用操作行为给封装为各类关键字。
所有的函数,如果不添加return,最后会返回None
将常用的自行封装到自定义类中,在使用时,直接调用自定义封装的类即可。
(1)创建临时driver (2)构造函数 (3)访问url
(4)定位元素 (5)点击操作 (6)输入 (7)退出 (8)显式等待 (9)强制等待
(10)切换Iframe (11)切换default窗体 (12)相对定位器 (13)句柄的切换 (14)断言文本信息
'''

# 自定义关键字驱动类
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from test008.test0083.ui_demo.conf.chrome_options import ChromeOptions

# 基于type_值决定生成的driver对象是什么类型
def open_browser(type_):
if type_ == 'Chrome':
driver = webdriver.Chrome(options=ChromeOptions().options())
else:
try:
driver = getattr(webdriver, type_)()
# 等同于 getattr(txt='Chrome')
except Exception as e:
print("Exception Information:" + str(e))
# 默认启动Chrome的浏览器
driver = webdriver.Chrome()
return driver


'''
Python反射机制:
四大内置函数:常用的是其中的getattr函数,就是获取指定类的指定属性
getattr(类,属性) 相当于 类.属性 的意思
例如:
webdriver.Chrome == getattr(webdriver,'Chrome')
基于反射获取属性是这个反射函数的基本定义。获取函数就需要在末尾加上()
例如:
webdriver.Chrome() == getattr(webdriver,'Chrome')()
例如open_browser()函数:
不用反射的形态
if type_ == Chrome:
driver = webdriver.Chrome()
elif type_ == Firefox:
driver = webdriver.Firefox()
elif type_ == Ie:
driver = webdriver.Ie()
elif safari
elif edge
'''


class Keys:
# 创建临时driver:driver = webdriver.Chrome()

# 构造函数
def __init__(self, txt, log):
self.driver = open_browser(txt)
self.driver.implicitly_wait(10)
self.log = log

# 访问url
def open(self, txt):
self.driver.get(txt)

# 定位元素
def locate(self, name, value):
return self.driver.find_element(name, value)

# 输入
def input(self, name, value, txt):
self.locate(name, value).send_keys(txt)

# 点击操作
def click(self, name, value):
self.locate(name, value).click()

# 退出
def quit(self):
self.driver.quit()

# 显式等待
def web_el_wait(self, name, value):
return WebDriverWait(self.driver, 10, 0.5).until(
lambda el: self.locate(name, value), message='元素查找失败')

# 强制等待
def wait(self, txt):
sleep(txt)

# 切换Iframe
def switch_frame(self, value, name=None):
if name is None:
self.driver.switch_to.frame(value)
else:
self.driver.switch_to.frame(self.locate(name, value))

# 切换default窗体
def switch_default(self):
self.driver.switch_to.default_content()

# 相对定位器
# def locator_with(self, method, value, el_name, el_value, direction):
# el = self.locate(el_name, el_value)
# direction_dict = {
# 'left': 'to_left_of', # 左侧
# 'right': 'to_right_of', # 右侧
# 'above': 'above', # 上方
# 'below': 'below', # 下方
# 'near': 'near' # 靠近
# }
# if isinstance(method, str):
# method_dict = {
# "id": By.ID,
# "xpath": By.XPATH,
# "link text": By.LINK_TEXT,
# "partial link text": By.PARTIAL_LINK_TEXT,
# "name": By.NAME,
# "tag name": By.TAG_NAME,
# "class name": By.CLASS_NAME,
# "css selector": By.CSS_SELECTOR
# }
# self.locate(locate_with(By.TAG_NAME, 'input').to_left_of(el))
# return self.driver.find_element(getattr(
# locate_with(method_dict.get(method), value), direction_dict.get(direction))(el))

# 句柄的切换(考虑不同场景的不同切换)
def switch_handle(self, close=False, index=1):
handles = self.driver.window_handles
if close:
self.driver.close()
self.driver.switch_to.window(handles[index])

# 断言文本信息:可以捕获异常进行处理,也可以不捕获,因为报错就相当于断言失败。
def assert_text(self, name, value, expect):
try:
reality = self.locate(name, value).text
assert expect == reality, '断言失败,实际结果为:{}'.format(reality)
return True
except Exception as e:
self.log.exception('出现异常,断言失败:实际结果与预期不符合:{}'.format(e))
return False

《五》框架的主入口
(1)main.py
 '''
程序主入口:
1. 调用excel读取执行测试用例
2. 读取指定路径下所有的excel测试用例
3. 初始化日志器与关键类
'''
import os

from conf import log_conf
from excel_driver import excel_read

if __name__ == '__main__':
# 创建日志生成器
log = log_conf.get_log('conf/log.ini')

# 定义测试用例List
cases = []
# 获取指定路径下的所有测试用例:用例集中在 test_data/ 的路径下
for path, dir, files in os.walk('./test_data'):
# 保存获取的所有测试用例文件:其实就是后缀名为xlsx的文件
for file in files:
# 获取文件信息
file_name = os.path.splitext(file)[0]
file_type = os.path.splitext(file)[1]
'''
file_type = os.path.splitext(file)
文件类型:excel_driver_old.xlsx(弃用的测试用例)
mylog.log(异常的测试用例)
test.ddd.xlsx(正常的测试用例)
输出结果:('excel_driver_old','.xlsx')
('mylog','.log')
('test.ddd','.xlsx')

file_type = os.path.splitext(file)[0] ( file_type = os.path.splitext(file)[1] )
文件类型:excel_driver_old.xlsx(弃用的测试用例)
mylog.log(异常的测试用例)
test.ddd.xlsx(正常的测试用例)
输出结果:(excel_driver_old) (.xlsx)
(mylog) (.log)
(test.ddd) (.xlsx)
'''
# 判断文件是否为excel(excel的后缀名为:.xlsx)
if file_type == '.xlsx':
# 判断是否需要添加该用例
if 'old' not in file_name:
cases.append(path + '/' + file)
else:
log.info('文件类型错误:{}'.format(file))

# 调用excel_driver中的run函数,运行测试用例
for case in cases:
log.info('正在运行{}测试用例'.format(case))
excel_read.run(case, log)
posted on   隆江猪脚饭  阅读(150)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示