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。逻辑运算符用于测定变量或值之间的逻辑。