什么是混合框架,混合框架就是将数据驱动与关键字驱动结合在一起,主要用来回归业务主流程,将核心流程串联起来。
上一篇我们写到了关键字驱动框架,关键字驱动框架是针对一个业务场景的单条测试用例的。
我们以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文件,即实现了混合框架。