背景痛点:
落地接口自动化项目过程中,提高团队内部的编码效率和代码可读性一直是重中之重,并且,在自动化用例运行期间,如遇到运行异常或断言失败等问题,如果运行的用例数量过大,在一定程度上,影响了团队内排查定位问题的效率。并且,运行时的具体情况细节,如果仅打印在控制台,当下一次运行开始时,之前的记录都不会得到妥善保存。
罗列痛点问题如下:
一、如果运行过程中遇到问题导致运行中断,新手同学需要从控制台看到实际运行报错的Traceback,再去找到对应的代码行位置,去排查问题;
二、基于问题一,很多新手同学可能会认为,在编码过程中,使用print打印各个请求步骤,进行用例运行的跟踪,但这无疑增加了编码负担;
三、print输出到控制台的结果显示不够具备美观性,在控制台定位问题眼花缭乱
四、运行的结果只在控制台打印,没有规范化日志文件保存,不会保留上一次运行日志;
针对这些痛点问题,需要对接口自动化框架的运行情况增加日志记录跟踪功能,方便定位问题,保留日志记录,并且提高编码效率。
实现细节
首先,团队内部需要定义好日志输出规范,定义各个等级的日志,实现step、info、error级别日志。
其次规范化日志输出格式,记录运行时间和运行类、函数、代码行号,并格式化输出,对输出文本美化,并且按照日志等级分别按严重程度记录日志文件。
然后输出内容到项目logs文件夹下,按日期进行命名,生成.log文件
import functools
import os
import inspect
from datetime import datetime
from colorama import Fore
from main import DIR
def info(text):
stack = inspect.stack()
formatted_time = datetime.now().strftime('%H:%M:%S:%f')[:-3] # 定义日志输出时间
code_path = f"{os.path.basename(stack[1].filename)}:{stack[1].lineno}" # 当前执行文件的绝对路径和执行代码行号
content = f"[INFO]{formatted_time}-{code_path} >> {text}"
print(Fore.LIGHTBLUE_EX + content)
str_time = datetime.now().strftime("%Y%m%d")
with open(file=DIR + '\\logs\\' + f'{str_time}_info.log', mode='a', encoding='utf8') as f:
f.write(content + '\n')
def step(text):
stack = inspect.stack()
formatted_time = datetime.now().strftime('%H:%M:%S:%f')[:-3] # 定义日志输出时间
code_path = f"{os.path.basename(stack[1].filename)}:{stack[1].lineno}" # 当前执行文件的绝对路径和执行代码行号
content = f"[STEP]{formatted_time}-{code_path} >> {text}"
print(Fore.LIGHTGREEN_EX + content)
str_time = datetime.now().strftime("%Y%m%d")
with open(file=DIR + '\\logs\\' + f'{str_time}_info.log', mode='a', encoding='utf8') as f:
f.write(content + '\n')
def error(text):
stack = inspect.stack()
formatted_time = datetime.now().strftime('%H:%M:%S:%f')[:-3] # 定义日志输出时间
code_path = f"{os.path.basename(stack[1].filename)}:{stack[1].lineno}" # 当前执行文件的绝对路径和执行代码行号
content = f"[ERROR]{formatted_time}-{code_path} >> {text}"
print(Fore.RED + content)
str_time = datetime.now().strftime("%Y%m%d")
with open(file=DIR + '\\logs\\' + f'{str_time}_info.log', mode='a', encoding='utf8') as f:
f.write(content + '\n')
with open(file=DIR + '\\logs\\' + f'{str_time}_error.log', mode='a', encoding='utf8') as f:
f.write(content + '\n')
def case_log_init(func):
@functools.wraps(func)
def inner(*args, **kwargs):
class_name = args[0].__class__.__name__ # 获取类名
method_name = func.__name__ # 获取方法名
docstring = inspect.getdoc(func) # 获取方法注释
print(Fore.LIGHTRED_EX + '-----------------------------------------------')
info(
f"【用例{method_name}】开始执行------------------------------------------------")
info(f'Class Name:{class_name}')
info(f'Method Name:{method_name}')
info(f'Test Description:{docstring}')
func(*args, **kwargs)
info(f"【用例{method_name}】运行结束------------------------------------------------")
return inner
def class_case_log(cls):
"""用例的日志装饰器级别"""
for name, method in inspect.getmembers(cls, inspect.isfunction):
if name.startswith('testCase'):
setattr(cls, name, case_log_init(method))
return cls
最后运用装饰器,给测试用例类增加日志输出功能,测试用例步骤中以step("日志内容")的形式进行调用日志记录功能方法。
这样,框架日志记录功能已实现完毕,下面验证下可用性...
验证可用性
控制台输出如下
生成日志文件如下
可以看到,按照日志级别输出日志,日志生成时间,运行位置(类、函数、代码行号)和运行步骤,请求信息等,都被准确记录到日志文件中持久保存。至此,痛点问题均被解决。