首页 > 其他 > 详细

** UI自动化混合驱动框架:关键字驱动与数据驱动

时间:2021-02-22 20:29:08      阅读:38      评论:0      收藏:0      [点我收藏+]

1. 工程结构说明

2. 代码示例

action

test_script

util

conf

test_data

screenshot_path

log

 

 

1. 工程结构说明

技术分享图片

 

2. 代码示例

(涉及的用户名与密码已脱敏,由xxx替代)

action/keyword.py

  1 from selenium import webdriver
  2 import time
  3 from conf.global_var import *
  4 from util.find_element_util import *
  5 from util.ini_parser import *
  6 
  7 
  8 # 初始化浏览器
  9 def init_browser(browser_name):
 10     if browser_name.lower() == "chrome":
 11         driver = webdriver.Chrome(CHROME_DRIVER)
 12     elif browser_name.lower() == "firefox":
 13         driver = webdriver.Firefox(FIREFOX_DRIVER)
 14     elif browser_name.lower() == "ie":
 15         driver = webdriver.Ie(IE_DRIVER)
 16     else:
 17         return "Error browser name!"
 18     return driver
 19 
 20 
 21 # 访问指定url
 22 def visit(driver, url):
 23     driver.get(url)
 24 
 25 
 26 # 输入操作
 27 def input(driver, locate_method, locate_exp, value):
 28     # 方式1:直接传定位方式和定位表达式
 29     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 30                              "partial link text", "css selector"]:
 31         find_element(driver, locate_method, locate_exp).send_keys(value)
 32     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 33     else:
 34         parser = IniParser(ELEMENT_FILE_PATH)
 35         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 36         find_element(driver, locate_method, locate_exp).send_keys(value)
 37 
 38 
 39 # 点击操作
 40 def click(driver, locate_method, locate_exp):
 41     # 方式1:直接传定位方式和定位表达式
 42     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 43                              "partial link text", "css selector"]:
 44         find_element(driver, locate_method, locate_exp).click()
 45     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 46     else:
 47         parser = IniParser(ELEMENT_FILE_PATH)
 48         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 49         find_element(driver, locate_method, locate_exp).click()
 50 
 51 
 52 # 清空输入框操作
 53 def clear(driver, locate_method, locate_exp):
 54     # 方式1:直接传定位方式和定位表达式
 55     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 56                              "partial link text", "css selector"]:
 57         find_element(driver, locate_method, locate_exp).clear()
 58     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 59     else:
 60         parser = IniParser(ELEMENT_FILE_PATH)
 61         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 62         find_element(driver, locate_method, locate_exp).clear()
 63 
 64 
 65 # 切换frame
 66 def switch_frame(driver, locate_method, locate_exp):
 67     # 方式1:直接传定位方式和定位表达式
 68     if locate_method in ["id", "xpath", "classname", "name", "tagname", "linktext",
 69                              "partial link text", "css selector"]:
 70         driver.switch_to.frame(find_element(driver, locate_method, locate_exp))
 71     # 方式2:通过ini文件的key找到value,再分割定位方式和定位表达式
 72     else:
 73         parser = IniParser(ELEMENT_FILE_PATH)
 74         locate_method, locate_exp = tuple(parser.get_value(locate_method, locate_exp).split(">"))
 75         driver.switch_to.frame(find_element(driver, locate_method, locate_exp))
 76 
 77 
 78 # 切换主frame
 79 def switch_home_frame(driver):
 80     driver.switch_to.default_content()
 81 
 82 
 83 # 断言
 84 def assert_word(driver, keyword):
 85     assert keyword in driver.page_source
 86 
 87 
 88 # 休眠
 89 def sleep(driver, times):
 90     time.sleep(int(times))
 91 
 92 
 93 # 关闭浏览器
 94 def quit(driver):
 95     driver.quit()
 96 
 97 
 98 if __name__ == "__main__":
 99     driver = init_browser("chrome")
100     visit(driver, "http://mail.126.com")
101     switch_frame(driver, "xpath", "//iframe[contains(@id,‘x-URS-iframe‘)]")
102     clear(driver, "xpath", "//input[@name=‘email‘]")
103     input(driver, "xpath", "//input[@name=‘email‘]", "juno3550")
104     input(driver, "xpath", "//input[@name=‘password‘]", "258978aa")
105     click(driver, "id", "dologin")
106     sleep(driver, 2)
107     assert_word(driver, "退出")
108     # 通过配置文件传参
109     quit(driver)
110     driver = init_browser("chrome")
111     visit(driver, "http://mail.126.com")
112     switch_frame(driver, "126mail_indexPage", "indexPage.frame")
113     clear(driver, "126mail_indexPage", "indexPage.username")
114     input(driver, "126mail_indexPage", "indexPage.username", "xxx")
115     input(driver, "126mail_indexPage", "indexPage.password", "xxx")
116     click(driver, "126mail_indexPage", "indexPage.loginbutton")
117     sleep(2)
118     assert_word(driver, "退出")
119     quit(driver)

 

test_script/main_script.py

  1 import traceback
  2 import re
  3 import queue
  4 import threading
  5 from action.keyword import *
  6 from util.excel_util import Excel
  7 from util.screenshot import take_screenshot
  8 from util.datetime_util import *
  9 from util.log_util import *
 10 
 11 
 12 # 测试步骤sheet用例的执行函数
 13 def execute_case(driver, test_data_file_path, test_script_sheet_name, test_data, head_flag=True):
 14     """
 15     :param driver: selenium的driver对象
 16     :param test_data_file_path: 测试数据excel文件名称 或 excel实例化对象
 17     :param test_script_sheet_name: 测试步骤的sheet名称
 18     :param test_data: 测试数据sheet中的指定行数据,格式如[{"登录用户名": "xxx", "登录密码": "xxx", ...}, {...}, ...]
 19     :param head_flag: 是否在测试结果sheet中写入标题行,默认只写入一次标题行
 20     :return: 指定行的用例/用例集的执行结果
 21     """
 22     case_suite_result = "成功"
 23     # 如果传入的是excel对象,则无需再次初始化;多次初始化会导致后续写入失败
 24     if isinstance(test_data_file_path, Excel):
 25         excel = test_data_file_path
 26     # 初始化excel工具类对象
 27     else:
 28         excel = Excel(test_data_file_path)
 29     excel.change_sheet(test_script_sheet_name)
 30     all_row_data = excel.get_all_row_data()
 31     excel.change_sheet("测试结果")
 32     # 在测试结果sheet中,标题行仅写入一次
 33     if head_flag:
 34         excel.write_row_data(all_row_data[0], "red")
 35     # 除标题行外,遍历所有行数据
 36     for row_data in all_row_data[1:]:
 37         # 若某行用例是其他sheet的用例集,则先递归执行完其他sheet的用例
 38         if "other_test_cases" in row_data[TEST_SCRIPT_KEYWORD_COL]:
 39             case_result = execute_case(driver, excel, row_data[TEST_SCRIPT_VALUE_COL], test_data, False)
 40         else:
 41             keyword = row_data[TEST_SCRIPT_KEYWORD_COL]
 42             locate_method = row_data[TEST_SCRIPT_LOCATE_METHOD_COL]
 43             locate_exp = row_data[TEST_SCRIPT_LOCATE_EXP_COL]
 44             value = row_data[TEST_SCRIPT_VALUE_COL]
 45             # 数据驱动,将${}格式的值替换为数据集(test_data)中的数据进行执行
 46             if re.search(r"\$\{(.*)\}", str(value)):
 47                 key = re.search(r"\$\{(.*)\}", str(value)).group(1)
 48                 # 如[{"登录用户名": "xxx", "登录密码": "xxx", ...}, {...}, ...]
 49                 value = test_data[key]
 50                 # 在测试结果sheet中,也要将${}格式的值替换为数据集(test_data)中的数据
 51                 row_data[TEST_SCRIPT_VALUE_COL] = value
 52             # 存在4种关键字执行函数类型
 53             if locate_method and locate_exp:
 54                 if value:
 55                     command = "{}(driver, ‘{}‘, ‘{}‘, ‘{}‘)".format(keyword, locate_method, locate_exp, value)
 56                 else:
 57                     command = "{}(driver, ‘{}‘, ‘{}‘)".format(keyword, locate_method, locate_exp)
 58             else:
 59                 if value:
 60                     command = "{}(driver, ‘{}‘)".format(keyword, value)
 61                 else:
 62                     command = "{}(driver)".format(keyword)
 63             try:
 64                 # 执行关键字函数用例
 65                 eval(command)
 66                 info("用例执行成功:{}".format(command))
 67                 case_result = "成功"
 68             except:
 69                 error("用例执行失败:{}".format(command))
 70                 traceback.print_exc()
 71                 # 写入本用例的执行结果
 72                 case_result = "失败"
 73                 # 写入本用例集的执行结果
 74                 case_suite_result = "失败"
 75                 # 进行截图
 76                 screenshot_file_path = take_screenshot(driver)
 77                 # 截图路径记录
 78                 row_data[TEST_SCRIPT_SCREENSHOT_PATH_COL] = screenshot_file_path
 79                 # 异常信息记录
 80                 row_data[TEST_SCRIPT_EXCEPTION_INFO_COL] = traceback.format_exc()
 81         # 测试时间记录
 82         row_data[TEST_SCRIPT_TEST_TIME_COL] = get_english_datetime()
 83         # 测试结果记录
 84         row_data[TEST_SCRIPT_TEST_RESULT_COL] = case_result
 85         # 将测试结果写入excel
 86         excel.write_row_data(row_data)
 87         excel.save()
 88     # 返回测试集合的测试结果(只要有一条用例不通过,整个测试集合均不通过)
 89     return case_suite_result
 90 
 91 
 92 # 获取测试数据的执行函数
 93 # 每行数据作为一个字典,存储在一个列表中。如[{"登录用户名": "xxx", "登录密码": "xxx", ...}, {...}, ...]
 94 def get_dict_test_data(test_data_file_path, test_data_sheet_name):
 95     """
 96     :param test_data_file_path: 测试数据excel文件名称 或 excel实例化对象
 97     :param test_data_sheet_name: 测试数据的sheet名称
 98     :return: 测试数据sheet中的所有数据行,格式为{标题: 值}
 99     """
100     # 如果传入的是excel对象,则无需再次初始化
101     if isinstance(test_data_file_path, Excel):
102         excel = test_data_file_path
103     else:
104         excel = Excel(test_data_file_path)
105     excel.change_sheet(test_data_sheet_name)
106     result = []
107     all_row_data = excel.get_all_row_data()
108     keys = all_row_data[0]
109     for row_data in all_row_data[1:]:
110         dict = {}
111         for i in range(len(keys)):
112             dict[keys[i]] = row_data[i]
113         result.append(dict)
114     return result
115 
116 
117 # 混合驱动用例的执行函数
118 # 测试步骤sheet与测试数据sheet分离(封装基础用例执行函数与获取测试数据执行函数)
119 def mix_case_execute(browser_name, test_data_file_path, test_script_sheet_name,
120                      test_data_sheet_name=None, head_flag=True):
121     """
122     :param browser_name: 需要初始化的浏览器名称
123     :param test_data_file_path: 测试数据excel文件名称 或 excel实例化对象
124     :param test_script_sheet_name: 测试步骤的sheet名称
125     :param test_data_sheet_name: 测试数据的sheet名称
126     :param head_flag: 是否在测试结果sheet中写入标题行,默认只写入一次标题行
127     :return: 指定行的用例/用例集的执行结果
128     """
129     case_result = "成功"
130     if test_data_sheet_name:
131         test_data = get_dict_test_data(test_data_file_path, test_data_sheet_name)
132         for data in test_data:
133             info("测试用例数据:{}".format(data))
134             driver = init_browser(browser_name)
135             result = execute_case(driver, test_data_file_path, test_script_sheet_name, data, head_flag)
136             if result == "失败":
137                 case_result = "失败"
138     else:
139         driver = init_browser(browser_name)
140         result = execute_case(driver, test_data_file_path, test_script_sheet_name, None, head_flag)
141         if result == "失败":
142             case_result = "失败"
143     return case_result
144 
145 
146 # 封装混合驱动用例的执行函数
147 # 测试步骤sheet与测试数据sheet由主测试用例sheet提供
148 def main_case_execute(test_data_file_path, main_case_sheet_name):
149     """
150     :param test_data_file_path: 测试数据excel文件名称
151     :param main_case_sheet_name: 测试用例的主sheet名称
152     :return: None
153     """
154     excel = Excel(test_data_file_path)
155     excel.change_sheet(main_case_sheet_name)
156     all_row_data = excel.get_all_row_data()
157     for row_data in all_row_data[1:]:
158         if row_data[MAIN_CASE_IS_EXECUTE_COL].lower() == "y":
159             case_name = row_data[MAIN_CASE_CASE_NAME_COL]
160             info("开始执行测试用例集【{}】".format(case_name))
161             if row_data[MAIN_CASE_DATA_SHEET_COL]:
162                 test_result = mix_case_execute(row_data[MAIN_CASE_BROWSER_NAME_COL], excel,
163                                                row_data[MAIN_CASE_SCRIPT_SHEET_COL], row_data[MAIN_CASE_DATA_SHEET_COL])
164             else:
165                 test_result = mix_case_execute(row_data[MAIN_CASE_BROWSER_NAME_COL], excel,
166                                                row_data[MAIN_CASE_SCRIPT_SHEET_COL], row_data[MAIN_CASE_DATA_SHEET_COL])
167         else:
168             continue
169         excel.change_sheet("测试结果")
170         excel.write_row_data(all_row_data[0], "red")
171         row_data[MAIN_CASE_TEST_TIME_COL] = get_english_datetime()
172         row_data[MAIN_CASE_TEST_RESULT_COL] = test_result
173         excel.write_row_data(row_data)
174         excel.save()
175 
176 
177 # 多线程任务函数:
178 def thread_task(q):
179     """
180     :param q: 队列(存储的是测试数据文件)
181     :return: None
182     """
183     try:
184         if not isinstance(q, queue.Queue):
185             warning("多线程任务函数入参有误:需为队列对象!")
186             return
187         while q.qsize() > 0:
188             test_data_file, main_case_sheet = q.get()
189             main_case_execute(test_data_file, main_case_sheet)
190     except:
191         error("多线程任务函数执行异常!")
192         traceback.print_exc()
193 
194 
195 # 多线程执行函数
196 def thread_main(thread_data, thread_num):
197     """
198     :param thread_data: 多线程任务函数所需要的测试数据集(二维数组)参数
199     :param thread_num: 需要开启的线程数
200     :return: None
201     """
202     try:
203         if not isinstance(thread_data, list):
204             warning("多线程任务函数入参有误:需为数组对象!")
205             return
206         for li in thread_data:
207             if not isinstance(li, list):
208                 warning("多线程任务函数入参有误:需为二维数组对象!")
209                 return
210         if not (isinstance(thread_num, int) and thread_num > 0):
211             warning("多线程任务函数入参有误:需为正整数对象!")
212             return
213         q = queue.Queue()
214         for data in thread_data:
215             q.put(tuple(data))
216         thread_list = []
217         # 初始化多线程
218         for i in range(thread_num):
219             t = threading.Thread(target=thread_task, args=(q,))
220             thread_list.append(t)
221         # 启动多线程
222         for t in thread_list:
223             t.start()
224         # 等待所有子线程结束
225         for t in thread_list:
226             t.join()
227     except:
228         error("多线程执行函数执行异常!")
229         traceback.print_exc()
230 
231 
232 if __name__ == "__main__":
233     # 基础用例的执行函数
234     # execute_case(driver, TEST_DATA_FILE_PATH_1, "添加联系人1")
235     # 获取测试数据的执行函数,如[{"登录用户名": "xxx", "登录密码": "xxx", ...}, {...}, ...]
236     # print(get_dict_test_data(TEST_DATA_FILE_PATH_1, "联系人数据"))
237     # 混合驱动用例的执行函数,测试步骤sheet与测试数据sheet分离(封装基础用例执行函数与获取测试数据执行函数)
238     # mix_case_execute("chrome", TEST_DATA_FILE_PATH_1, "添加联系人", "联系人数据")
239     # 封装混合驱动用例的执行函数,测试步骤sheet与测试数据sheet由主测试用例sheet提供
240     # main_case_execute(TEST_DATA_FILE_PATH_1, "测试用例")
241     # 多线程任务函数所需的数据集合
242     thread_data = [[TEST_DATA_FILE_PATH_1, "测试用例"], [TEST_DATA_FILE_PATH_2, "测试用例"],
243                    [TEST_DATA_FILE_PATH_3, "测试用例"]]
244     # 多线程执行函数
245     thread_main(thread_data, 3)

 

util/find_element_util.py

 1 from selenium.webdriver.support.ui import WebDriverWait
 2 
 3 
 4 # 显式等待一个元素
 5 def find_element(driver, locate_method, locate_exp):
 6     # 显式等待对象(最多等10秒,每0.2秒判断一次等待的条件)
 7     return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_element(locate_method, locate_exp))
 8 
 9 # 显式等待一组元素
10 def find_elements(driver, locate_method, locate_exp):
11     # 显式等待对象(最多等10秒,每0.2秒判断一次等待的条件)
12     return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_elements(locate_method, locate_exp))

 

util/excel_util.py

  1 from openpyxl import load_workbook
  2 from openpyxl.styles import PatternFill, Font, Side, Border
  3 import os
  4 
  5 
  6 class Excel:
  7 
  8     def __init__(self, test_data_file_path):
  9         # 文件格式校验
 10         if not os.path.exists(test_data_file_path):
 11             print("Excel工具类初始化失败:【{}】文件不存在!".format(test_data_file_path))
 12             return
 13         if not test_data_file_path.endswith(".xlsx") or not test_data_file_path.endswith(".xlsx"):
 14             print("Excel工具类初始化失败:【{}】文件非excel文件类型!".format(test_data_file_path))
 15             return
 16         # 打开指定excel文件
 17         self.wb = load_workbook(test_data_file_path)
 18         # 初始化默认sheet
 19         self.ws = self.wb.active
 20         # 保存文件时使用的文件路径
 21         self.test_data_file_path = test_data_file_path
 22         # 初始化红、绿色,供样式使用
 23         self.color_dict = {"red": "FFFF3030", "green": "FF008B00"}
 24 
 25     # 查看所有sheet名称
 26     def get_sheets(self):
 27         return self.wb.sheetnames
 28 
 29     # 根据sheet名称切换sheet
 30     def change_sheet(self, sheet_name):
 31         if sheet_name not in self.get_sheets():
 32             print("sheet切换失败:【{}】指定sheet名称不存在!".format(sheet_name))
 33             return
 34         self.ws = self.wb.get_sheet_by_name(sheet_name)
 35 
 36     # 返回当前sheet的最大行号
 37     def max_row_num(self):
 38         return self.ws.max_row
 39 
 40     # 返回当前sheet的最大列号
 41     def max_col_num(self):
 42         return self.ws.max_column
 43 
 44     # 获取指定行数据(设定索引从0开始)
 45     def get_one_row_data(self, row_no):
 46         if row_no < 0 or row_no > self.max_row_num()-1:
 47             print("输入的行号【{}】有误:需在0至最大行数之间!".format(row_no))
 48             return
 49         # API的索引从1开始
 50         return [cell.value for cell in self.ws[row_no+1]]
 51 
 52     # 获取指定列数据
 53     def get_one_col_data(self, col_no):
 54         if col_no < 0 or col_no > self.max_col_num()-1:
 55             print("输入的列号【{}】有误:需在0至最大列数之间!".format(col_no))
 56             return
 57         return [cell.value for cell in tuple(self.ws.columns)[col_no+1]]
 58 
 59     # 获取当前sheet的所有行数据
 60     def get_all_row_data(self):
 61         result = []
 62         # # API的索引从1开始
 63         for row_data in self.ws[1:self.max_row_num()]:
 64             result.append([cell.value for cell in row_data])
 65         return result
 66 
 67     # 追加一行数据
 68     def write_row_data(self, data, fill_color=None, font_color=None, border=True):
 69         if not isinstance(data, (list, tuple)):
 70             print("追加的数据类型有误:需为列号或元组类型!【{}】".format(data))
 71             return
 72         self.ws.append(data)
 73         # 添加字体颜色
 74         if font_color:
 75             if font_color in self.color_dict.keys():
 76                 font_color = self.color_dict[font_color]
 77             # 需要设置的单元格长度应与数据长度一致,否则默认与之前行的长度一致
 78         count = 0
 79         for cell in self.ws[self.max_row_num()]:
 80             if count > len(data) - 1:
 81                 break
 82             # cell不为None,才能设置样式
 83             if cell:
 84                 if cell.value in ["pass", "成功"]:
 85                     cell.font = Font(color=self.color_dict["green"])
 86                 elif cell.value in ["fail", "失败"]:
 87                     cell.font = Font(color=self.color_dict["red"])
 88                 else:
 89                     cell.font = Font(color=font_color)
 90             count += 1
 91         # 添加背景颜色
 92         if fill_color:
 93             if fill_color in self.color_dict.keys():
 94                 fill_color = self.color_dict[fill_color]
 95             count = 0
 96             for cell in self.ws[self.max_row_num()]:
 97                 if count > len(data) - 1:
 98                     break
 99                 if cell:
100                     cell.fill = PatternFill(fill_type="solid", fgColor=fill_color)
101                 count += 1
102         # 添加单元格边框
103         if border:
104             bd = Side(style="thin", color="000000")
105             count = 0
106             for cell in self.ws[self.max_row_num()]:
107                 if count > len(data) - 1:
108                     break
109                 if cell:
110                     cell.border = Border(left=bd, right=bd, top=bd, bottom=bd)
111                 count += 1
112 
113     # 保存文件
114     def save(self):
115         self.wb.save(self.test_data_file_path)
116 
117 
118 if __name__ == "__main__":
119     from conf.global_var import *
120     excel = Excel(TEST_DATA_FILE_PATH)
121     excel.change_sheet("登录1")
122     # print(excel.get_all_row_data())
123     excel.write_row_data((1,2,"嘻哈",None,"ddd"), "red", "green")
124     excel.save()

 

util/ini_parser.py

 1 import configparser
 2 
 3 
 4 class IniParser:
 5 
 6     # 初始化打开指定ini文件并指定编码
 7     def __init__(self, file_path):
 8         self.cf = configparser.ConfigParser()
 9         self.cf.read(file_path, encoding="utf-8")
10 
11     # 获取所有分组名称
12     def get_sections(self):
13         return self.cf.sections()
14 
15     # 获取指定分组的所有键
16     def get_options(self, section):
17         return self.cf.options(section)
18 
19     # 获取指定分组的键值对
20     def get_items(self, section):
21         return self.cf.items(section)
22 
23     # 获取指定分组的指定键的值
24     def get_value(self, section, key):
25         return self.cf.get(section, key)
26 
27 
28 if __name__ == "__main__":
29     from conf.global_var import *
30     parser = IniParser(ELEMENT_FILE_PATH)
31     print(parser.get_sections())
32     print(parser.get_options("126mail_indexPage"))
33     print(parser.get_value("126mail_indexPage", indexpage.frame))

 

util/datetime_util.py

 1 import time
 2 
 3 
 4 # 返回中文格式的日期:xxxx年xx月xx日
 5 def get_chinese_date():
 6     year = time.localtime().tm_year
 7     if len(str(year)) == 1:
 8         year = "0" + str(year)
 9     month = time.localtime().tm_mon
10     if len(str(month)) == 1:
11         month = "0" + str(month)
12     day = time.localtime().tm_mday
13     if len(str(day)) == 1:
14         day = "0" + str(day)
15     return "{}年{}月{}日".format(year, month, day)
16 
17 
18 # 返回英文格式的日期:xxxx/xx/xx
19 def get_english_date():
20     year = time.localtime().tm_year
21     if len(str(year)) == 1:
22         year = "0" + str(year)
23     month = time.localtime().tm_mon
24     if len(str(month)) == 1:
25         month = "0" + str(month)
26     day = time.localtime().tm_mday
27     if len(str(day)) == 1:
28         day = "0" + str(day)
29     return "{}/{}/{}".format(year, month, day)
30 
31 
32 # 返回中文格式的时间:xx时xx分xx秒
33 def get_chinese_time():
34     hour = time.localtime().tm_hour
35     if len(str(hour)) == 1:
36         hour = "0" + str(hour)
37     minute = time.localtime().tm_min
38     if len(str(minute)) == 1:
39         minute = "0" + str(minute)
40     second = time.localtime().tm_sec
41     if len(str(second)) == 1:
42         second = "0" + str(second)
43     return "{}时{}分{}秒".format(hour, minute, second)
44 
45 
46 # 返回英文格式的时间:xx:xx:xx
47 def get_english_time():
48     hour = time.localtime().tm_hour
49     if len(str(hour)) == 1:
50         hour = "0" + str(hour)
51     minute = time.localtime().tm_min
52     if len(str(minute)) == 1:
53         minute = "0" + str(minute)
54     second = time.localtime().tm_sec
55     if len(str(second)) == 1:
56         second = "0" + str(second)
57     return "{}:{}:{}".format(hour, minute, second)
58 
59 
60 # 返回中文格式的日期时间
61 def get_chinese_datetime():
62     return get_chinese_date() + " " + get_chinese_time()
63 
64 
65 # 返回英文格式的日期时间
66 def get_english_datetime():
67     return get_english_date() + " " + get_english_time()
68 
69 
70 if __name__ == "__main__":
71     print(get_chinese_datetime())
72     print(get_english_datetime())

 

util/log_util.py

 1 import logging
 2 import logging.config
 3 from conf.global_var import *
 4 
 5 
 6 # 日志配置文件:多个logger,每个logger指定不同的handler
 7 # handler:设定了日志输出行的格式
 8 #          以及设定写日志到文件(是否回滚)?还是到屏幕
 9 #          还定了打印日志的级别
10 logging.config.fileConfig(LOG_CONF_FILE_PATH)
11 logger = logging.getLogger("example01")
12 
13 
14 def debug(message):
15     logging.debug(message)
16 
17 
18 def info(message):
19     logging.info(message)
20 
21 
22 def warning(message):
23     logging.warning(message)
24 
25 
26 def error(message):
27     logging.error(message)
28 
29 
30 if __name__=="__main__":
31     debug("hi")
32     info("hiphop")
33     warning("hello")
34     error("这是一个error日志")

 

util/screenshot.py

 1 import traceback
 2 import os
 3 from util.datetime_util import *
 4 from conf.global_var import *
 5 
 6 
 7 # 截图函数
 8 def take_screenshot(driver):
 9     # 创建当前日期目录
10     dir = os.path.join(SCREENSHOT_PATH, get_chinese_date())
11     if not os.path.exists(dir):
12         os.makedirs(dir)
13     # 以当前时间为文件名
14     file_name = get_chinese_time()
15     file_path = os.path.join(dir, file_name+".png")
16     try:
17         driver.get_screenshot_as_file(file_path)
18         # 返回截图文件的绝对路径
19         return file_path
20     except:
21         print("截图发生异常【{}】".format(file_path))
22         traceback.print_exc()
23         return file_path

 

conf/global_var.py

 1 import os
 2 
 3 
 4 # 工程根路径
 5 PROJECT_ROOT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 6 
 7 # 元素定位方法的ini配置文件路径
 8 ELEMENT_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "ElementsRepository.ini")
 9 
10 # excel文件路径
11 TEST_DATA_FILE_PATH_1 = os.path.join(PROJECT_ROOT_PATH, "test_data", "混合驱动_测试用例_1.xlsx")
12 TEST_DATA_FILE_PATH_2 = os.path.join(PROJECT_ROOT_PATH, "test_data", "混合驱动_测试用例_2.xlsx")
13 TEST_DATA_FILE_PATH_3 = os.path.join(PROJECT_ROOT_PATH, "test_data", "混合驱动_测试用例_3.xlsx")
14 
15 # 驱动路径
16 CHROME_DRIVER = "E:\\auto_test_driver\\chromedriver.exe"
17 IE_DRIVER = "E:\\auto_test_driver\\IEDriverServer.exe"
18 FIREFOX_DRIVER = "E:\\auto_test_driver\\geckodriver.exe"
19 
20 # 截图路径
21 SCREENSHOT_PATH = os.path.join(PROJECT_ROOT_PATH, "screenshot_path")
22 
23 # 日志配置文件路径
24 LOG_CONF_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "Logger.conf")
25 
26 # 测试步骤sheet的列号
27 TEST_SCRIPT_KEYWORD_COL = 2
28 TEST_SCRIPT_LOCATE_METHOD_COL = 3
29 TEST_SCRIPT_LOCATE_EXP_COL = 4
30 TEST_SCRIPT_VALUE_COL = 5
31 TEST_SCRIPT_TEST_TIME_COL = 6
32 TEST_SCRIPT_TEST_RESULT_COL = 7
33 TEST_SCRIPT_EXCEPTION_INFO_COL = 8
34 TEST_SCRIPT_SCREENSHOT_PATH_COL = 9
35 
36 # 主测试用例sheet列号
37 MAIN_CASE_CASE_NAME_COL = 3
38 MAIN_CASE_BROWSER_NAME_COL = 5
39 MAIN_CASE_SCRIPT_SHEET_COL = 6
40 MAIN_CASE_DATA_SHEET_COL = 7
41 MAIN_CASE_IS_EXECUTE_COL = 8
42 MAIN_CASE_TEST_TIME_COL = 9
43 MAIN_CASE_TEST_RESULT_COL = 10
44 
45 
46 if __name__ == "__main__":
47     print(PROJECT_ROOT_PATH)

 

conf/ElementsRepository.ini

 1 [126mail_indexPage]
 2 indexPage.loginlink=xpath>//a[contains(text(),密码登录)]
 3 indexPage.frame=xpath>//iframe[contains(@id,x-URS-iframe)]
 4 indexPage.username=xpath>//input[@name=email]
 5 indexPage.password=xpath>//input[@name=password]
 6 indexPage.loginbutton=id>dologin
 7 
 8 [126mail_homePage]
 9 homePage.addressLink=xpath>//div[text()=通讯录]
10 
11 [126mail_contactPersonPage]
12 contactPersonPage.createButton=xpath>//span[text()=新建联系人]
13 contactPersonPage.name=xpath>//a[@title=编辑详细姓名]/preceding-sibling::div/input
14 contactPersonPage.email=xpath>//*[@id=iaddress_MAIL_wrap]//input
15 contactPersonPage.starContacts=xpath>//span[text()=设为星标联系人]/preceding-sibling::span/b
16 contactPersonPage.phone=xpath>//*[@id=iaddress_TEL_wrap]//dd//input
17 contactPersonPage.otherinfo=xpath>//textarea
18 contactPersonPage.confirmButton=xpath>//span[.=确 定]

 

conf/Logger.conf

 1 #logger.conf
 2 ###############################################
 3 [loggers]
 4 keys=root,example01,example02
 5 [logger_root]
 6 level=DEBUG
 7 handlers=hand01,hand02
 8 
 9 [logger_example01]
10 handlers=hand01,hand02
11 qualname=example01
12 propagate=0
13 
14 [logger_example02]
15 handlers=hand01,hand03
16 qualname=example02
17 propagate=0
18 
19 ###############################################
20 [handlers]
21 keys=hand01,hand02,hand03
22 
23 [handler_hand01]
24 class=StreamHandler
25 level=INFO
26 formatter=form01
27 args=(sys.stderr,)
28 
29 [handler_hand02]
30 class=FileHandler
31 level=DEBUG
32 formatter=form01
33 args=(..\\log\\Mail126TestLogfile.log, a)
34 
35 [handler_hand03]
36 class=handlers.RotatingFileHandler
37 level=INFO
38 formatter=form01
39 args=(..\\log\\Mail126TestLogfile.log, a, 10*1024*1024, 5)
40 
41 ###############################################
42 [formatters]
43 keys=form01,form02
44 
45 [formatter_form01]
46 format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
47 datefmt=%Y-%m-%d %H:%M:%S
48 
49 [formatter_form02]
50 format=%(name)-12s: %(levelname)-8s %(message)s
51 datefmt=%Y-%m-%d %H:%M:%S

 

测试数据文件(excel版)

主执行用例sheet

技术分享图片

 测试步骤用例sheet

技术分享图片

技术分享图片

 测试数据sheet

(供测试步骤sheet的值使用)

技术分享图片

测试结果记录sheet

(分别打印主测试用例和测试步骤用例的测试结果)

技术分享图片

 

screenshot_path

(异常截图保存目录)

技术分享图片

 

log/Mail126TestLogfile.log

(日志输出文件)

2021-02-21 00:20:56 log_util.py[line:19] INFO 开始执行测试用例集【登录流程】
2021-02-21 00:21:07 log_util.py[line:19] INFO 用例执行成功:visit(driver, https://www.126.com)
2021-02-21 00:21:07 log_util.py[line:19] INFO 用例执行成功:switch_frame(driver, 126mail_indexPage, indexPage.frame)
2021-02-21 00:21:08 log_util.py[line:19] INFO 用例执行成功:clear(driver, 126mail_indexPage, indexPage.username)
2021-02-21 00:21:08 log_util.py[line:19] INFO 用例执行成功:input(driver, 126mail_indexPage, indexPage.username, xxx)
2021-02-21 00:21:09 log_util.py[line:19] INFO 用例执行成功:input(driver, 126mail_indexPage, indexPage.password, xxx)
2021-02-21 00:21:09 log_util.py[line:19] INFO 用例执行成功:click(driver, 126mail_indexPage, indexPage.loginbutton)
2021-02-21 00:21:09 log_util.py[line:19] INFO 用例执行成功:switch_home_frame(driver)
2021-02-21 00:21:12 log_util.py[line:19] INFO 用例执行成功:sleep(driver, 3)
2021-02-21 00:21:13 log_util.py[line:19] INFO 用例执行成功:assert_word(driver, 退出)
2021-02-21 00:21:17 log_util.py[line:19] INFO 用例执行成功:quit(driver)
2021-02-21 00:21:18 log_util.py[line:19] INFO 开始执行测试用例集【联系人添加流程】
2021-02-21 00:21:18 log_util.py[line:19] INFO 测试用例数据:{登录用户名: xxx, 登录密码: xxx, 名字: 张三, 邮件地址: zhangsan@qq.com, 电话: 13301111111, 其他信息: 张三的信息}
2021-02-21 00:21:28 log_util.py[line:19] INFO 用例执行成功:visit(driver, https://www.126.com)
2021-02-21 00:21:29 log_util.py[line:19] INFO 用例执行成功:switch_frame(driver, 126mail_indexPage, indexPage.frame)
2021-02-21 00:21:29 log_util.py[line:19] INFO 用例执行成功:clear(driver, 126mail_indexPage, indexPage.username)
2021-02-21 00:21:30 log_util.py[line:19] INFO 用例执行成功:input(driver, 126mail_indexPage, indexPage.username, xxx)
......
......

 

** UI自动化混合驱动框架:关键字驱动与数据驱动

原文:https://www.cnblogs.com/juno3550/p/14431902.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!