什么是混合框架,混合框架就是将数据驱动与关键字驱动结合在一起,主要用来回归业务主流程,将核心流程串联起来。
上一篇我们写到了关键字驱动框架,关键字驱动框架是针对一个业务场景的单条测试用例的。
我们以163邮箱的登录到创建联系人这个流程为例,来看看混合框架是怎样的。
首先准备一个存放测试用例和数据的excel文件,文件内容如下:
测试用例的sheet页:case
mock表示这条测试用例我们需要用到的框架模型,key表示关键字,data表示数据
step_sheet表示这条用例我们需要用到的关键字驱动的sheet页名称
data_sheet表示这条用例我们需要用到的数据驱动的sheet页名称
login_step页:
add_person_step页:添加联系人的步骤
add_person_data页:添加联系人所需要用到的数据
excel的准备工作就完成了,接下来看代码:
首先是项目目录:只写了简单的几个目录,其他的目录在pageobject三层架构中写过,可以参考,都是一样的。
Setting文件夹的Config.py文件:
# Config.py
import os
Base_Dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 测试数据文件
Test_Data_Path = os.path.join(Base_Dir, 'TestData')
Util文件夹的find_ele.py文件:
# find_ele.py
from selenium.webdriver.support.wait import WebDriverWait
def find_element(driver, location_type, location_express):
'''查找控件元素'''
try:
driver = WebDriverWait(driver, 20).until(lambda driver:driver.find_element(location_type, location_express))
return driver
except Exception as e:
raise e
def find_elements(driver, location_type, location_express):
'''查找元素组'''
try:
driver = WebDriverWait(driver, 20).until(lambda driver:driver.find_elements(location_type, location_express))
return driver
except Exception as e:
raise e
Util文件夹的excel_parse.py文件:读取excel的内容
# excel_parse.py
from Setting.Config import Test_Data_Path
from openpyxl import load_workbook
class ExcelParse:
def __init__(self):
self.workbook = None
# self.sheet = None
def load_workbook(self, filename):
'''加载文件'''
try:
self.workbook = load_workbook(filename)
except Exception as e:
raise e
def get_sheet(self, sheetname):
'''获取sheet页'''
try:
# self.sheet = self.workbook[sheetname]
return self.workbook[sheetname]
except Exception as e:
raise e
def get_row_num(self, sheet):
'''返回行数'''
# return self.sheet.max_row
return sheet.max_row
def get_col_num(self, sheet):
'''返回列数'''
# return self.sheet.max_column
return sheet.max_column
def get_cell_value(self, sheet, row, col):
'''返回某一单元格的值'''
# return self.sheet.cell(row=row, column=col).value
return sheet.cell(row=row, column=col).value
def get_row_value(self, sheet, row):
'''返回某一行的值'''
try:
col = self.get_col_num(sheet)
data = []
for i in range(1, col+1):
data.append(self.get_cell_value(sheet, row, i))
return data
except Exception as e:
raise e
def write_cell(self, sheet, row, col, filename, content):
'''单元格赋值'''
try:
# self.sheet.cell(row=row, column=col, value=content)
sheet.cell(row=row, column=col, value=content)
self.workbook.save(filename)
except Exception as e:
raise e
if __name__ == '__main__':
execl = ExcelParse()
execl.load_workbook(Test_Data_Path + '/test_data.xlsx')
sheet = execl.get_sheet('case')
# execl.get_sheet('login')
res = execl.get_row_value(sheet, 2)
print(res)
Util文件夹的elementAction.py文件:执行动作的封装
# elementAction.py
import time
from selenium import webdriver
from Util.find_ele import find_element, find_elements
driver = None
def open_browse(browser_name, *args):
'''打开浏览器'''
global driver
try:
if browser_name.lower() == 'chrome':
driver = webdriver.Chrome()
elif browser_name.lower() == 'firefox':
driver = webdriver.Firefox()
else:
driver = webdriver.Ie()
except Exception as e:
raise e
def get_url(url, *args):
'''打开网址'''
try:
driver.get(url)
except Exception as e:
raise e
def max_window(*args):
'''窗口最大化'''
try:
driver.maximize_window()
except Exception as e:
raise e
def switch_frame(location_type, location_express, *args):
'''切换iframe'''
try:
frame = find_element(driver, location_type, location_express)
driver.switch_to.frame(frame)
except Exception as e:
raise e
def input_content(location_type, location_express, content, *args):
'''定位输入框,输入内容'''
try:
find_element(driver, location_type, location_express).send_keys(content)
except Exception as e:
raise e
def input_subject(location_type, location_express, input_conetnt, *args):
'''定位输入框,输入内容'''
try:
# location_express的值为:
location_express, index = location_express.split(',')
find_elements(driver, location_type, location_express)[int(index)].send_keys(input_conetnt)
except Exception as e:
raise e
def switch_default(*args):
'''返回默认iframe'''
try:
driver.switch_to.default_content()
except Exception as e:
raise e
def click(location_type, location_express, *args):
'''点击操作'''
try:
find_element(driver, location_type, location_express).click()
except Exception as e:
raise e
def assert_title(title, *args):
'''断言title是否正确'''
try:
assert title in driver.title
except Exception as e:
raise e
def close_browse():
'''关闭浏览器'''
driver.quit()
def sleep(sec):
'''等待'''
time.sleep(sec)
if __name__ == '__main__':
open_browse('chrome')
get_url('http://mail.163.com')
max_window()
switch_frame('tag name', 'iframe')
input_content('name', 'email', 'YM_yimin')
input_content('name', 'password', 'yimin19960930')
click('id', 'dologin')
assert_title('网易')
Util文件夹的common.py文件:封装拼接的执行动作函数
# common.py
def generate_method_express(location_type, location_express, key_word, operate_data):
# location_type, location_express为空,operate_data不为空
if key_word and operate_data and location_type is None and location_express is None:
# 判断操作值的类型
if isinstance(operate_data, int):
method_express = key_word + '(' + str(operate_data) + ')'
else:
method_express = key_word + "('" + operate_data + "')"
# print(method_express)
# 只有关键字有值,其他的都为空,比如:max_window, close_browse
elif key_word and operate_data is None and location_type is None and location_express is None:
method_express = key_word + '()'
# print(method_express)
# location_type,location_express不为空,operate_data为空
elif key_word and location_type and location_express and operate_data is None:
method_express = key_word + "('" + location_type + "','" + location_express + "')"
# print(method_express)
# 都不为空
else:
if isinstance(operate_data, int):
method_express = key_word + "('" + location_type + "','" + location_express + "'," + str(operate_data) + ")"
else:
method_express = key_word + "('" + location_type + "','" + location_express + "','" + operate_data + "')"
print(method_express)
return method_express
TestScript文件夹下的add_contractor.py文件:添加联系人的测试用例执行
# 添加联系人
import time
from Util.common import generate_method_express
from Util.excel_parse import ExcelParse
from Setting.Config import Test_Data_Path
from Util.elementAction import *
from Util import elementAction
from Util.find_ele import find_element
def add_contractors(excel, stepSheet, dataSheet):
'''添加联系人'''
# 数据源行数
data_row_nums = excel.get_row_num(dataSheet)
# 步骤行数
step_row_nums = excel.get_row_num(stepSheet)
# 成功的步骤数
success_record = 0
# 数据驱动sheet页中需要执行的行数
need_run_record = 0
# 遍历数据驱动sheet页中的数据
for i in range(2, data_row_nums):
# 判断数据驱动sheet页的数据是否需要执行
if excel.get_cell_value(dataSheet, i, 6).lower() == 'y':
need_run_record += 1
# 将这一行的数据全部拿出来
name = excel.get_cell_value(dataSheet, i, 1) # 姓名
email = excel.get_cell_value(dataSheet, i, 2) # 邮箱
is_star = excel.get_cell_value(dataSheet, i, 3) # 是否星标
phone = excel.get_cell_value(dataSheet, i, 4) # 电话号码
remarks = excel.get_cell_value(dataSheet, i, 2) # 备注
success_step = 0 # 记录每行数据成功的步骤数
# 编辑关键字驱动sheet页中的步骤
for j in range(2, step_row_nums):
# 获取关键字驱动sheet页中的每行数据
step_desc = excel.get_cell_value(stepSheet, j, 2) # 步骤描述
location_type = excel.get_cell_value(stepSheet, j, 3) # 定位方式
location_express = excel.get_cell_value(stepSheet, j, 4) # 定位方式表达式
keyword = excel.get_cell_value(stepSheet, j, 5) # 关键字
operate_value = excel.get_cell_value(stepSheet, j, 6) # 操作值
# 当操作值是变量的时候,要引用数据源(数据驱动sheet页)中的数据
# operate_value的值是字符串,并且以${开头,}结尾, 例如operate_value='${name}'
if isinstance(operate_value, str) and operate_value.startswith('${') and operate_value.endswith('}'):
# 把operate_value中的变量名截取出来
operate_value = eval(operate_value[2:operate_value.index('}')])
# 组装函数,拼接每个步骤的执行动作函数
func_express = generate_method_express(location_type, location_express, keyword, operate_value)
# 当step_desc为星标是否选择时,当operate_value等于Y(即执行点击操作),选中星标,当operate_value等于Y,不选中星标(即不执行点击操作)
# func_express = click(location_type, location_express, 'Y/N')
if operate_value != 'no_star':
# 执行选中星标,点击操作
try:
eval(func_express)
except Exception as e:
raise e
else:
# 执行选中星标操作,没有异常,成功步骤+1
success_step += 1
else:
# 不执行选中星标操作,要记录成功步骤数
success_step += 1
# 判断成功步骤数,与关键字驱动sheet页的步骤数是否相等
if success_step+1 == step_row_nums:
# 成功步骤数+1 等于 关键字驱动sheet页的行数, 成功的数据+1
success_record += 1
# 将成功的结果写入数据驱动sheet页对应的单元格
excel.write_cell(dataSheet, i, 7, Test_Data_Path+'/test_data.xlsx', 'pass')
else:
excel.write_cell(dataSheet, i, 7, Test_Data_Path + '/test_data.xlsx', 'fail')
# 数据驱动sheet页中的所有数据全部轮训执行完之后
# 判断成功记录数success_record 和 需要执行的数据need_run_record 相等,则说明该测试用例执行成功
if success_record == need_run_record:
return 'Pass'
else:
return 'Fail'
if __name__ == '__main__':
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('http://mail.163.com')
frame = find_element(driver, 'tag name', 'iframe')
driver.switch_to.frame(frame)
find_element(driver, 'name', 'email').send_keys('test123')
find_element(driver, 'name', 'password').send_keys('a123456')
find_element(driver, 'id', 'dologin').click()
time.sleep(5)
elementAction.driver = driver
execl = ExcelParse()
execl.load_workbook(Test_Data_Path + '/test_data.xlsx')
step_sheet = execl.get_sheet('add_person_step')
data_sheet = execl.get_sheet('add_person_data')
add_contractors(execl,step_sheet, data_sheet)
TestScript文件夹的test_login_add_send.py文件:读取case页的测试用例,进行执行
# test_login_add_send.py
from Util.excel_parse import ExcelParse
from Setting.Config import Test_Data_Path
from TestScript.add_contractor import add_contractors
from Util.common import generate_method_express
def test_loginAndAddAndSend():
try:
# 获取数据文件case中的内容
excel = ExcelParse()
excel.load_workbook(Test_Data_Path + '/test_data.xlsx')
case_sheet = excel.get_sheet('case') # 获取测试用例sheet页
case_nums = excel.get_row_num(case_sheet) # case的总行数
# 遍历case中的数据
for i in range(2, case_nums+1):
# 判断该用例是否要执行
if excel.get_cell_value(case_sheet, i, 7) == 'y':
# 获取用例名称
case_name = excel.get_cell_value(case_sheet, i, 2)
# 框架类型
frame_mode = excel.get_cell_value(case_sheet, i, 4)
# 步骤sheet名
step_sheet_name = excel.get_cell_value(case_sheet, i, 5)
stepsheet = excel.get_sheet(step_sheet_name) # 获取步骤sheet页
if frame_mode == 'data':
# 如果框架类型为data,获取数据sheet名
data_sheet_name = excel.get_cell_value(case_sheet, i, 6)
# 分别获取两个sheet,作为参数传入
datasheet = excel.get_sheet(data_sheet_name)
result = None
# 调用对应的方法,即添加联系人的方法
if case_name == 'add_person':
result = add_contractors(excel, stepsheet, datasheet)
if result == 'Pass':
excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Pass')
else:
excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Fail')
elif frame_mode == 'key':
# 获取步骤数
step_nums = excel.get_row_num(stepsheet)
# 记录成功的步骤数
success_step_num = 0
for j in range(2, step_nums+1):
# 步骤描述
step_desc = excel.get_cell_value(stepsheet, j, 2)
location_type = step_desc = excel.get_cell_value(stepsheet, j, 3)
location_express = step_desc = excel.get_cell_value(stepsheet, j, 4)
key_word = excel.get_cell_value(stepsheet, j, 5)
operate_value = step_desc = excel.get_cell_value(stepsheet, j, 6)
# 构建函数表达式
func_express = generate_method_express(location_type, location_express, key_word, operate_value)
# 执行函数, 不抛出异常就认为执行成功
try:
print(f'开始执行 {step_desc}')
eval(func_express)
except Exception as e:
print(f'执行{step_desc}发生异常{e}')
# 某一步发生异常,则该用例执行失败,将失败结果写入测试用例(case)sheet页
excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Fail')
else:
success_step_num += 1
# 执行成功步骤数+1 = 步骤总数,用例执行成功
if success_step_num+1 == step_nums:
# 写入成功
excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Pass')
else:
excel.write_cell(case_sheet, i, 8, Test_Data_Path + '/test_data.xlsx', 'Skip')
except Exception as e:
raise e
最后执行test_login_add_send.py文件,即实现了混合框架。