lucialee 2020-01-12
# 日志类的封装 import logging from logging.handlers import RotatingFileHandler from class_13_0111_rewrite_unittest.config_handle import do_config class HandleLog: """ 日志类的封装 """ def __init__(self): self.case_logger = logging.getLogger(do_config(‘log‘, ‘logger_name‘)) self.case_logger.setLevel(do_config(‘log‘, ‘logger_level‘)) console_handle = logging.StreamHandler() file_handle = RotatingFileHandler(filename=do_config(‘log‘, ‘log_filename‘), maxBytes=do_config(‘log‘, ‘maxBytes‘), backupCount=do_config(‘log‘, ‘backupCount‘), encoding=‘utf-8‘) console_handle.setLevel(do_config(‘log‘, ‘console_level‘)) file_handle.setLevel(do_config(‘log‘, ‘file_level‘)) simple_formatter = logging.Formatter(do_config(‘log‘, ‘simple_formatter‘)) verbose_formatter = logging.Formatter(do_config(‘log‘, ‘verbose_formatter‘)) console_handle.setFormatter(simple_formatter) file_handle.setFormatter(verbose_formatter) self.case_logger.addHandler(console_handle) self.case_logger.addHandler(file_handle) def get_logger(self): """ 获取 logger 日志器对象 :return: """ return self.case_logger do_log = HandleLog().get_logger() if __name__ == ‘__main__‘: for _ in range(1000): do_log.debug(‘这是 debug 级别的日志。‘) do_log.info(‘这是 info 级别的日志。‘) do_log.warning(‘这是 warning 级别的日志。‘) do_log.error(‘这是 error 级别的日志。‘) do_log.critical(‘这是 critical 级别的日志。‘)
日志类的封装
# excel 封装成类 from openpyxl import load_workbook from collections import namedtuple from class_13_0111_rewrite_unittest.config_handle import do_config class HandleExcel(object): """ 定义处理 excel 的类 """ # config = HandleConfig() # 定义一个类属性,创建 HandleConfig() 对象 def __init__(self, filename, sheetname=None): self.filename = filename self.sheetname = sheetname # 定义一个空列表,存放所有 cases 命名元组对象 self.case_list = [] # 打开 excel 文件 self.wb = load_workbook(self.filename) # 定位表单 if self.sheetname is None: # 如果没有传 sheetname 这个参数,就默认获取第一个表单 self.ws = self.wb.active else: self.ws = self.wb[self.sheetname] # 取 excel 表头,构造出来一个元组 self.sheet_head_tuple = tuple(self.ws.iter_rows(max_row=1, values_only=True))[0] # 创建一个元组类 self.cases = namedtuple("cases", self.sheet_head_tuple, rename=True) # 三元运算 # self.ws = self.wb[self.sheetname] if self.sheetname is not None else self.wb.active def get_cases(self): """ 获取所有的测试用例 :return: 存放 cases 命名元组的列表 """ for row_data in self.ws.iter_rows(min_row=2, values_only=True): self.case_list.append(self.cases(*row_data)) return self.case_list def get_case(self, row): """ 获取某一条测试用例 :param row: 行号 :return: 一个 cases 命名元组对象 """ # 判断行号是否符合条件,(数字,和 行号早最大行和最小行之间) if isinstance(row, int) and (2 <= row <= self.ws.max_row): return tuple(self.ws.iter_rows(min_row=row, max_row=row, values_only=True))[0] # return self.case_list[row] # 这种方法不可取是因为,如果不调用 get_cases ,self.cases_list 就为空 else: print("传入行号参数有误!") def write_result(self, row, actual, result): """ 将实际值与测试用例执行的结果保存到 excel :param row: 行号 :param actual: 实际值 :param result: 测试结果: “Pass” or “Fail” :return: """ if isinstance(row, int) and (2 <= row <= self.ws.max_row): self.ws.cell(row=row, column=do_config(‘excel‘, ‘actual_col‘), value=actual) self.ws.cell(row=row, column=do_config(‘excel‘, ‘result_col‘), value=result) self.wb.save(self.filename) else: print("传入行号参数有误!") do_excel = HandleExcel(do_config(‘file name‘, ‘cases_path‘)) if __name__ == ‘__main__‘: # file_name = "cases.xlsx" # one_excel = HandleExcel(filename=file_name) cases = do_excel.get_cases() print(cases) one_case = do_excel.get_case(2) print(do_excel) do_excel.write_result(2, "ces", "测试")
excel处理 类的封装
# 封装配置文件的代码 from configparser import ConfigParser class HandleConfig(ConfigParser): """ 定义处理配置文件的类 """ def __init__(self): # 对父类的构造方法进行拓展 # 调用父类的构造方法 super().__init__() # 重写或者拓展父类的构造方法,往往我们需要先调用父类的构造方法 self.filename = ‘test_case.conf‘ self.read(self.filename, encoding=‘utf-8‘) # 读取配置文件 # self 是 ConfigParer 的对象,因为 HandleConfig 继承了父类,所以可以直接调用 def __call__(self, section=‘DEFAULT‘, option=None, is_eval=False, is_bool=False): """ ’对象()‘这种形式,__call__ 方法会被调用 :param section: 区域名 :param option: 选项名 :param is_eval: 为默认参数,是否进行 eval 函数转换,默认不转换 :param is_bool: 选项所对应的值是否需要转换为 bool 类型,默认不转换 :return: """ if option is None: # 1、一个对象() --> 返回 DEFAULT 默认区域下的所有选项,构造成一个字典 # 2、一个对象(区域名) --> 能够获取此区域下的所有选项,返回一个字典 return dict(self[section]) if isinstance(is_bool, bool): if is_bool: # 3、一个对象(区域名, 选项名,is_bool=True) --> 将获取到的数据使用 getboolean() 方法来获取 return self.getboolean(section, option) else: raise ValueError(‘is_bool 必须是 bool 类型。‘) # 手动抛出异常 data = self.get(section, option) # 4、一个对象(区域名, 选项名) --> 通过区域名以及选项名来获取值 # 5、如果获取到的数据为数字类型的字符串,自动转换为 python 中的 int 类型 if data.isdigit(): # 判断是否为数字类型的字符串 return int(data) try: return float(data) # 如果为浮点类型的字符串,则直接转换为 float 类型 except ValueError: pass if isinstance(is_eval, bool): if is_eval: # 6、一个对象(is_eval=True) --> 将获取到的数据使用 eval 函数进行转换 return eval(data) else: raise ValueError(‘is_eval 必须是 bool 类型。‘) return data do_config = HandleConfig() if __name__ == ‘__main__‘: config = HandleConfig() print(config()) print(config(‘excel‘)) a = config(‘excel‘, ‘two_res‘) # print(config(‘excel‘, ‘actual_col‘)) print(f‘值为:{a}\n类型为:{type(a)}‘) b = config(‘excel‘, ‘two_res‘, is_bool=True) # print(config(‘excel‘, ‘two_res‘, is_bool=True)) print(f‘值为:{b}\n类型为:{type(b)}‘) c = config(‘excel‘, ‘five_res‘, is_eval=True) print(f‘值为:{c}\n类型为:{type(c)}‘) # print(config(‘excel‘, ‘five_res‘, is_eval=True))
配置文件类的封装
import unittest import inspect from ddt import ddt, data from class_06_1209_unittest_02.math_operation import MathOperation from openpyxl import load_workbook from collections import namedtuple from class_13_0111_rewrite_unittest.excel_handle import do_excel from class_13_0111_rewrite_unittest.config_handle import do_config from class_13_0111_rewrite_unittest.log_handle import do_log @ddt class TestMulti(unittest.TestCase): """ 测试两数相乘 """ # handle_config = HandleConfig() # handle_excel = HandleExcel(do_config(‘file name‘, ‘cases_path‘)) cases = do_excel.get_cases() # 获取所有的用例,返回一个嵌套命名元组的列表 @classmethod def setUpClass(cls) -> None: """ 重写父类的类方法 所有用例执行之前,会被调用一次 --> 整个测试类调用之前,执行一次 :return: """ do_log.info("{:=^40s}\n".format("开始执行用例!")) @classmethod def tearDownClass(cls) -> None: """ 重写父类的类方法 所有用例执行之后,会被调用一次 --> 整个测试类调用之后,执行一次 :return: """ do_log.info("{:=^40s}\n".format("用例执行结束!")) @data(*cases) def test_multi(self, data_namedtuple): """ 两数相乘 :return: """ # 能够通过 inspect.stack 方法查看当前运行的实例名称 do_log.info(f"Running Test Method: {inspect.stack()[0][3]}\n") case_id = data_namedtuple.case_id msg = data_namedtuple.title l_date = data_namedtuple.l_date r_date = data_namedtuple.r_date expected = data_namedtuple.expected # 获取实际结果 real_result = MathOperation(l_date, r_date).mul() # 预期结果 expect_result = expected # msg run_success_msg = do_config(‘msg‘, ‘success_result‘) run_fail_msg = do_config(‘msg‘, ‘fail_result‘) # 断言 try: self.assertEqual(expect_result, real_result, msg="{}失败!".format(msg)) except AssertionError as e: # 记录日志 do_log.error("{},执行结果为:{}\n具体异常为:{}\n".format(msg, run_fail_msg, e)) # 将执行失败的信息写入到文件 do_excel.write_result(row=case_id+1, actual=real_result, result=run_fail_msg) raise e else: # self.file.write("{},执行结果为:{}\n".format(msg, run_success_msg)) # 将执行成功的信息写入到文件 do_log.info("{},执行结果为:{}\n".format(msg, run_success_msg)) do_excel.write_result(row=case_id+1, actual=real_result, result=run_success_msg) if __name__ == ‘__main__‘: unittest.main()
测试脚本
# 注释不要和区域名和选项名写在一行 # 文件路径配置 [file name] # cases_path 为测试用例 excel 文件的路径 # log_path 为记录日志的文件路径 cases_path = cases.xlsx log_path = run_result_1.txt report_html_name = test_result # 提示信息 [msg] ; success_result 为用例执行成功的提示信息 ; fail_path 为用例执行失败的提示信息 success_result = Pass fail_result = Fail # excel 相关配置 [excel] # actual_col 为将用例执行的实际结果存储到 excel 中的列号 # result_col 为将用例执行的结果存储到 excel 中的列号 actual_col = 6 result_col = 7 # 日志相关配置 [log] # 日志器名称 logger_name = case # 日志器日志等级 logger_level = DEBUG # 日志文件名 log_filename = case.log # 一个日志文件的最大字节数 maxBytes = 4096 # 备份文件数 backupCount = 3 # 输出到控制台的日志等级 console_level = ERROR # 输出到文件的日志等级 file_level = INFO # 控制台使用的日志简单格式 # 配置文件出现 % ,需要使用 % 来转义 simple_formatter = %%(asctime)s - [%%(levelname)s] - [日志信息]:%%(message)s # 日志文件使用日志复杂格式 verbose_formatter = %%(asctime)s - [%%(levelname)s] - %%(module)s - %%(name)s - %%(lineno)d - [日志信息]:%%(message)s
配置文件
运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。以上实例中 7、5 和 12 是操作数。关系运算符用于计算结果是否为 true 或者 false。逻辑运算符用于测定变量或值之间的逻辑。