parent
7a0e17b104
commit
8adf8e964a
File diff suppressed because one or more lines are too long
|
@ -14,7 +14,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['get_current_date', 'get_current_time', 'get_delta_time']
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def get_current_date(fmt="%Y-%m-%d"):
|
||||
"""
|
||||
获取当前日期,默认格式为:%Y-%m-%d
|
||||
|
@ -27,7 +27,7 @@ def get_current_date(fmt="%Y-%m-%d"):
|
|||
return datetime.now().strftime(fmt)
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def get_current_time(fmt="%Y-%m-%d %H:%M:%S"):
|
||||
"""
|
||||
获取当前时间:默认格式为:%Y-%m-%d %H:%M:%S
|
||||
|
@ -40,7 +40,7 @@ def get_current_time(fmt="%Y-%m-%d %H:%M:%S"):
|
|||
return datetime.now().strftime(fmt)
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def get_delta_time(days=0, hours=0, minutes=0, seconds=0, fmt="%Y-%m-%d %H:%M:%S"):
|
||||
"""
|
||||
获取当前时间指定间隔后的时间
|
||||
|
|
|
@ -14,7 +14,7 @@ __all__ = ['md5_encryption']
|
|||
from common.bif_functions import logger
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def md5_encryption(raw_str, sha_str='', toupper=False):
|
||||
"""
|
||||
执行md5加密
|
||||
|
|
|
@ -14,7 +14,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['json_dumps', 'json_loads']
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def json_dumps(obj):
|
||||
"""
|
||||
Serialize ``obj`` to a JSON formatted ``str``.
|
||||
|
@ -27,7 +27,7 @@ def json_dumps(obj):
|
|||
return json.dumps(obj, ensure_ascii=False)
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def json_loads(obj):
|
||||
"""
|
||||
Deserialize ``obj`` (a ``str``, ``bytes`` or ``bytearray`` instance containing a JSON document) to a Python object.
|
||||
|
|
|
@ -12,7 +12,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['list_slice', 'sublist']
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def list_slice(obj, index=None, start=None, end=None, step=1):
|
||||
"""
|
||||
切片方法
|
||||
|
@ -37,7 +37,7 @@ def list_slice(obj, index=None, start=None, end=None, step=1):
|
|||
return None
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def sublist(raw_list, start=None, end=None):
|
||||
"""
|
||||
截取子列表
|
||||
|
|
|
@ -16,7 +16,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['random_choice', 'gen_random_num', 'gen_random_str', 'random_gps']
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def random_choice(args):
|
||||
"""
|
||||
随机选择
|
||||
|
@ -29,7 +29,7 @@ def random_choice(args):
|
|||
return random.choice(args)
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def gen_random_num(length):
|
||||
"""
|
||||
随机生成指定长度的数字
|
||||
|
@ -42,7 +42,7 @@ def gen_random_num(length):
|
|||
return random.randint(int('1' + '0' * (int(length) - 1)), int('9' * int(length)))
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def gen_random_str(length):
|
||||
"""
|
||||
生成指定长度的随机字符串
|
||||
|
|
|
@ -15,7 +15,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['regex_extract']
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def regex_extract(string, pattern, group=None):
|
||||
"""
|
||||
根据正则表达式提取内容
|
||||
|
|
|
@ -31,7 +31,7 @@ def substr(raw_str, start=None, end=None):
|
|||
return ''
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def str_join(obj, connector=","):
|
||||
"""
|
||||
连接任意数量的字符
|
||||
|
|
|
@ -14,7 +14,7 @@ from common.bif_functions import logger
|
|||
__all__ = ['get_timestamp', 'ms_fmt_hms']
|
||||
|
||||
|
||||
@logger.log_decorator("错误原因:时间戳的长度只能在10到16位之间,默认返回长度为13位的时间戳")
|
||||
@logger.catch
|
||||
def get_timestamp(length=13):
|
||||
"""
|
||||
获取时间戳字符串,长度最多为16位;默认13位
|
||||
|
@ -34,7 +34,7 @@ def get_timestamp(length=13):
|
|||
get_timestamp(13)
|
||||
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def ms_fmt_hms(ms):
|
||||
"""
|
||||
将毫秒转换成 h:m:s.ms格式字符串
|
||||
|
|
|
@ -1,65 +1,66 @@
|
|||
from functools import wraps
|
||||
|
||||
|
||||
def _create_test_name(index, name, title):
|
||||
"""
|
||||
Create a new test name based on index and name.
|
||||
:param index: Index for generating the test name.
|
||||
:param name: Base name for the test.
|
||||
:param title: Base title for the test.
|
||||
:return: Generated test name.
|
||||
"""
|
||||
test_name = f"{name}_{index + 1:03}_{title}"
|
||||
return test_name
|
||||
def _create_test_name(index,name, title):
|
||||
"""
|
||||
Create a new test name based on index and name.
|
||||
:param index: Index for generating the test name.
|
||||
:param name: Base name for the test.
|
||||
:param title: Base title for the test.
|
||||
:return: Generated test name.
|
||||
"""
|
||||
test_name = f"{name}_{index + 1:03}_{title}"
|
||||
return test_name
|
||||
|
||||
|
||||
def _set_function_attributes(func, original_func, new_name, test_desc):
|
||||
"""
|
||||
Set attributes of a function.
|
||||
:param func: The function to set attributes for.
|
||||
:param original_func: The original function being wrapped.
|
||||
:param new_name: New name for the function.
|
||||
:param test_desc: New documentation for the function.
|
||||
"""
|
||||
func.__wrapped__ = original_func
|
||||
func.__name__ = new_name
|
||||
func.__doc__ = test_desc
|
||||
"""
|
||||
Set attributes of a function.
|
||||
:param func: The function to set attributes for.
|
||||
:param original_func: The original function being wrapped.
|
||||
:param new_name: New name for the function.
|
||||
:param test_desc: New documentation for the function.
|
||||
"""
|
||||
func.__wrapped__ = original_func
|
||||
func.__name__ = new_name
|
||||
func.__doc__ = test_desc
|
||||
|
||||
|
||||
def _update_func(new_func_name, params, test_desc, func, *args, **kwargs):
|
||||
"""
|
||||
Create a wrapper function with updated attributes.
|
||||
:param new_func_name: New name for the wrapper function.
|
||||
:param params: Test parameters.
|
||||
:param test_desc: Test description.
|
||||
:param func: Original function to be wrapped.
|
||||
:param args: Additional positional arguments for the function.
|
||||
:param kwargs: Additional keyword arguments for the function.
|
||||
:return: Wrapped function.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(self):
|
||||
return func(self, params, *args, **kwargs)
|
||||
|
||||
_set_function_attributes(wrapper, func, new_func_name, test_desc)
|
||||
return wrapper
|
||||
"""
|
||||
Create a wrapper function with updated attributes.
|
||||
:param new_func_name: New name for the wrapper function.
|
||||
:param params: Test parameters.
|
||||
:param test_desc: Test description.
|
||||
:param func: Original function to be wrapped.
|
||||
:param args: Additional positional arguments for the function.
|
||||
:param kwargs: Additional keyword arguments for the function.
|
||||
:return: Wrapped function.
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def wrapper(self):
|
||||
return func(self, params, *args, **kwargs)
|
||||
|
||||
_set_function_attributes(wrapper, func, new_func_name, test_desc)
|
||||
return wrapper
|
||||
|
||||
|
||||
def ddt(cls):
|
||||
"""
|
||||
:param cls: 测试类
|
||||
:return:
|
||||
"""
|
||||
for func_name, func in list(cls.__dict__.items()):
|
||||
if hasattr(func, "PARAMS"):
|
||||
for index, case_data in enumerate(getattr(func, "PARAMS")):
|
||||
name = str(case_data.get("Name", "缺少Name"))
|
||||
test_desc = str(case_data.get("Description", "缺少Description"))
|
||||
new_test_name = _create_test_name(index, func_name, name)
|
||||
func2 = _update_func(new_test_name, case_data, test_desc, func)
|
||||
setattr(cls, new_test_name, func2)
|
||||
else:
|
||||
# Avoid name clashes
|
||||
delattr(cls, func_name)
|
||||
return cls
|
||||
"""
|
||||
:param cls: 测试类
|
||||
:return:
|
||||
"""
|
||||
for func_name, func in list(cls.__dict__.items()):
|
||||
if hasattr(func, "PARAMS"):
|
||||
for index, case_data in enumerate(getattr(func, "PARAMS")):
|
||||
name = str(case_data.get("Name", "缺少Name"))
|
||||
test_desc = str(case_data.get("Description", "缺少Description"))
|
||||
# new_test_name = _create_test_name(name)
|
||||
new_test_name = _create_test_name(index, func_name, name)
|
||||
func2 = _update_func(new_test_name, case_data, test_desc, func)
|
||||
setattr(cls, new_test_name, func2)
|
||||
else:
|
||||
# Avoid name clashes
|
||||
delattr(cls, func_name)
|
||||
return cls
|
||||
|
|
|
@ -28,7 +28,7 @@ class DataExtractor(Environments):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@logger.log_decorator("提取参数出现了意想不到的错误!!")
|
||||
@logger.catch
|
||||
def substitute_data(self, response, regex=None, keys=None, deps=None, jp_dict=None):
|
||||
"""
|
||||
数据提取
|
||||
|
|
|
@ -57,7 +57,7 @@ class DoExcel:
|
|||
sub_data[FieldNames.SHEET] = sheet_name
|
||||
yield sub_data
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def write_back(self, sheet_name, i, **kwargs):
|
||||
"""
|
||||
|
||||
|
@ -78,7 +78,7 @@ class DoExcel:
|
|||
sheet.cell(i + 1, 26).value = assertions
|
||||
self.wb.save(self.file_name)
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def clear_date(self):
|
||||
"""
|
||||
执行清空单元格数据
|
||||
|
@ -97,7 +97,7 @@ class DoExcel:
|
|||
self.wb.save(self.file_name)
|
||||
return f"清空指定 {sheets} 中的单元格成功"
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def get_excel_init(self):
|
||||
"""
|
||||
获取 excel 中 sheet 名称为 init 中的基础数据
|
||||
|
|
|
@ -128,7 +128,7 @@ class DoExcel:
|
|||
row_data.append(cell_value)
|
||||
return row_data
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def save(self, filename=None):
|
||||
"""
|
||||
获取文件名
|
||||
|
@ -145,7 +145,7 @@ class DoExcel:
|
|||
# print("保存成功")
|
||||
return True
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def do_main(self, output_filename=None, *data):
|
||||
"""
|
||||
动态保存列表嵌套字典的数据到 excel 中
|
||||
|
|
|
@ -6,116 +6,82 @@
|
|||
@desc: 日志封装
|
||||
"""
|
||||
import os
|
||||
from functools import wraps
|
||||
from time import perf_counter
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from common.utils.decorators import singleton
|
||||
from config.config import Config
|
||||
|
||||
LOG_DIR = Config.LOG_PATH
|
||||
|
||||
|
||||
@singleton
|
||||
class MyLogger:
|
||||
"""
|
||||
根据时间、文件大小切割日志
|
||||
"""
|
||||
|
||||
def __init__(self, log_dir=LOG_DIR, max_size=20, retention='7 days'):
|
||||
self.log_dir = log_dir
|
||||
self.max_size = max_size
|
||||
self.retention = retention
|
||||
self.logger = self.configure_logger()
|
||||
|
||||
def configure_logger(self):
|
||||
"""
|
||||
Returns:
|
||||
"""
|
||||
# 创建日志目录
|
||||
os.makedirs(self.log_dir, exist_ok=True)
|
||||
|
||||
shared_config = {
|
||||
"level": "ERROR",
|
||||
"enqueue": True,
|
||||
"backtrace": False,
|
||||
"format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
|
||||
}
|
||||
|
||||
# 添加按照日期和大小切割的文件 handler
|
||||
logger.add(
|
||||
sink=f"{self.log_dir}/{{time:YYYY-MM-DD}}.log",
|
||||
rotation=f"{self.max_size} MB",
|
||||
retention=self.retention,
|
||||
**shared_config
|
||||
)
|
||||
|
||||
# 配置按照等级划分的文件 handler 和控制台输出
|
||||
logger.add(sink=self.get_log_path, **shared_config)
|
||||
|
||||
return logger
|
||||
|
||||
def get_log_path(self, message: str) -> str:
|
||||
"""
|
||||
根据等级返回日志路径
|
||||
Args:
|
||||
message:
|
||||
Returns:
|
||||
"""
|
||||
log_level = message.record["level"].name.lower()
|
||||
log_file = f"{log_level}.log"
|
||||
log_path = os.path.join(self.log_dir, log_file)
|
||||
|
||||
return log_path
|
||||
|
||||
def __getattr__(self, level: str):
|
||||
return getattr(self.logger, level)
|
||||
|
||||
def log_decorator(self, msg="快看,异常了,别唧唧哇哇,快排查!!"):
|
||||
"""
|
||||
日志装饰器,记录函数的名称、参数、返回值、运行时间和异常信息
|
||||
Args:
|
||||
logger: 日志记录器对象
|
||||
Returns:
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
# self.logger.info(f'-----------分割线-----------')
|
||||
# self.logger.info(f'| 调用函数: {func.__name__} | args: {args} kwargs:{kwargs}')
|
||||
start = perf_counter() # 开始时间
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
end = perf_counter() # 结束时间
|
||||
duration = end - start
|
||||
# self.logger.info(f"| 结束调用函数: {func.__name__}, duration:{duration:4f}s")
|
||||
return result
|
||||
except Exception as e:
|
||||
self.logger.error(f"| called {func.__name__} | error: {msg}: {e}")
|
||||
|
||||
# self.logger.info(f"-----------分割线-----------")
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
"""
|
||||
根据时间、文件大小以及日志等级切割日志
|
||||
"""
|
||||
|
||||
def __init__(self, log_dir=Config.LOG_PATH):
|
||||
self.log_dir = log_dir
|
||||
self.logger = self.configure_logger()
|
||||
|
||||
def configure_logger(self):
|
||||
"""
|
||||
Returns:
|
||||
"""
|
||||
# 创建日志目录
|
||||
os.makedirs(self.log_dir, exist_ok=True)
|
||||
shared_config = {
|
||||
"level": "DEBUG",
|
||||
"enqueue": True,
|
||||
"backtrace": False,
|
||||
"encoding": "utf-8",
|
||||
"rotation": "50 MB",
|
||||
"retention": "7 days",
|
||||
"format": "{time:YYYY-MM-DD HH:mm:ss} | {level} | {file} | {module} | {message}",
|
||||
}
|
||||
# 添加按照等级划分以及日期和大小切割的文件 handler
|
||||
for level in ["INFO", "DEBUG", "WARNING", "ERROR", "CRITICAL"]:
|
||||
logger.add(
|
||||
sink=self.get_log_path(f"{level}.log"),
|
||||
filter=self.level_filter(level), # 使用 level_filter 方法
|
||||
**shared_config
|
||||
)
|
||||
return logger
|
||||
|
||||
def level_filter(self, level):
|
||||
"""过滤日志等级"""
|
||||
|
||||
def is_level(record):
|
||||
return record["level"].name == level
|
||||
|
||||
return is_level
|
||||
|
||||
def get_log_path(self, filename):
|
||||
return os.path.join(self.log_dir, filename)
|
||||
|
||||
def trace(self, msg):
|
||||
self.logger.trace(msg)
|
||||
|
||||
def __getattr__(self, level):
|
||||
return getattr(self.logger, level)
|
||||
|
||||
def catch(self, *args, **kwargs):
|
||||
return self.logger.catch(*args, **kwargs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
log = MyLogger()
|
||||
|
||||
|
||||
@log.log_decorator("勇哥也不知道错在哪里")
|
||||
def test_zero_division_error(a, b):
|
||||
return a / b
|
||||
|
||||
|
||||
for i in range(1000):
|
||||
log.error('错误信息')
|
||||
log.critical('严重错误信息')
|
||||
test_zero_division_error(1, 0)
|
||||
log.debug('调试信息')
|
||||
log.info('普通信息')
|
||||
log.success('成功信息')
|
||||
log.warning('警告信息')
|
||||
log = MyLogger()
|
||||
|
||||
|
||||
@log.catch
|
||||
def test_zero_division_error(a, b):
|
||||
return a / b
|
||||
|
||||
|
||||
for i in range(100):
|
||||
test_zero_division_error(1, 0)
|
||||
log.debug('调试信息')
|
||||
log.info('普通信息')
|
||||
log.success('成功信息')
|
||||
log.warning('警告信息')
|
||||
log.error('错误信息')
|
||||
log.critical('严重错误信息')
|
||||
|
|
|
@ -54,7 +54,10 @@ def request_retry_on_exception(retries=2, delay=1.5):
|
|||
print(f"| 请求 body --> {response.request.body}")
|
||||
print(f"| 接口状态--> {response.status_code}")
|
||||
print(f"| 接口耗时--> {response.elapsed}")
|
||||
print(f"| 接口响应--> {response.text}")
|
||||
try:
|
||||
print(f"| 接口响应--> {response.json()}")
|
||||
except:
|
||||
print(f"| 接口响应--> {response.text}")
|
||||
except Exception as error:
|
||||
print(f"| 第{i + 1}次请求参数=【{args}__{kwargs}】")
|
||||
# print(f"| 代码耗时--> {time.time() - st}")
|
||||
|
|
|
@ -119,7 +119,7 @@ class InvalidParameterFormatError(MyBaseException):
|
|||
def __init__(self, parameter_info, reason):
|
||||
msg = f"无效的参数格式异常:parameter_info={parameter_info},原因={reason}"
|
||||
super().__init__(msg)
|
||||
self.logger.error(msg)
|
||||
self.logger.warning(msg)
|
||||
|
||||
|
||||
class ResponseJsonConversionError(MyBaseException):
|
||||
|
@ -129,7 +129,7 @@ class ResponseJsonConversionError(MyBaseException):
|
|||
def __init__(self, response_text, reason):
|
||||
msg = f"响应内容转换为 JSON 格式异常:响应内容={response_text}, 原因={reason}"
|
||||
super().__init__(msg)
|
||||
self.logger.error(msg)
|
||||
self.logger.warning(msg)
|
||||
|
||||
|
||||
class DynamicLoadingError(MyBaseException):
|
||||
|
|
|
@ -18,7 +18,7 @@ class Loaders(HttpClient):
|
|||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def load_built_in_functions(self, model):
|
||||
"""
|
||||
加载bif_functions包中的内建方法
|
||||
|
@ -31,7 +31,7 @@ class Loaders(HttpClient):
|
|||
return built_in_functions
|
||||
|
||||
@staticmethod
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def load_built_in_comparators() -> object:
|
||||
"""
|
||||
加载包中的内建比较器
|
||||
|
@ -45,7 +45,7 @@ class Loaders(HttpClient):
|
|||
|
||||
return built_in_comparators
|
||||
|
||||
@logger.log_decorator()
|
||||
@logger.catch
|
||||
def set_bif_fun(self, model):
|
||||
"""
|
||||
加载内置方法
|
||||
|
|
|
@ -105,7 +105,7 @@ class Validator(Loaders):
|
|||
|
||||
"""
|
||||
if not validate_variables:
|
||||
self.assertions = '未填写预期结果,默认断言HTTP请求状态码!!!'
|
||||
self.assertions = ['未填写预期结果,默认断言HTTP请求状态码!!!']
|
||||
return
|
||||
self.validate_variables_list.clear()
|
||||
self.assertions.clear()
|
||||
|
|
|
@ -54,7 +54,7 @@ class Config:
|
|||
"url": "https://oapi.dingtalk.com/robot/send?access_token=7d1e11079e00a4ca9f11283f526349abd5ba3f792ef7bcb346909ff215af02de",
|
||||
"secret": "SEC441dbbdb8dbe150e5fc3e348bb449d3113b1be1a90be527b898ccd78c51566c1",
|
||||
"key": "", # 安全关键字
|
||||
"atMobiles": "xxxxx", # 需要@指定人员的手机号
|
||||
"atMobiles": "1827813600", # 需要@指定人员的手机号
|
||||
"isAtAll": True, # 是否@ 所有人
|
||||
"except_info": False # 是否发送测试不通过的异常数据
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
28
run.py
28
run.py
|
@ -12,23 +12,29 @@ sys.path.append("./")
|
|||
sys.path.append('src')
|
||||
|
||||
from config.config import Config
|
||||
from common.bif_functions.bif_datetime import get_current_time
|
||||
from common.core.testRunner import TestRunner
|
||||
from common.utils.decorators import install_dependencies
|
||||
|
||||
|
||||
@install_dependencies
|
||||
def run():
|
||||
test_report = Config.TEST_REPORT
|
||||
test_case = unittest.defaultTestLoader.discover(Config.SCRIPT, pattern="test_*.py")
|
||||
runner = TestRunner(test_case, report_dir=test_report, title="接口自动化测试报告", templates=2, tester="kira",
|
||||
desc="自动化测试")
|
||||
runner.run()
|
||||
# # get_failed_test_cases = runner.get_failed_test_cases()
|
||||
# 发送通知
|
||||
# runner.email_notice()
|
||||
# runner.dingtalk_notice()
|
||||
# runner.weixin_notice()
|
||||
test_report = Config.TEST_REPORT
|
||||
test_case = unittest.defaultTestLoader.discover(Config.SCRIPT, pattern="test_*.py")
|
||||
runner = TestRunner(test_case,
|
||||
report_dir=test_report,
|
||||
filename=Config.TEST_REPORT_FILE,
|
||||
title="接口自动化测试报告",
|
||||
templates=2,
|
||||
tester="kira",
|
||||
desc="自动化测试")
|
||||
runner.run()
|
||||
# # get_failed_test_cases = runner.get_failed_test_cases()
|
||||
# 发送通知
|
||||
# runner.email_notice()
|
||||
runner.dingtalk_notice()
|
||||
runner.weixin_notice()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
run()
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -298,7 +298,7 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</th>
|
||||
<th scope="col" style="width: 15%;padding: 0">测试方法</th>
|
||||
<th scope="col" style="width: 15%;padding: 0">测试模块</th>
|
||||
<th scope="col" style="width: 20%;padding: 0">用例描述</th>
|
||||
<th scope="col" style="width: 15%;padding: 0">执行时间</th>
|
||||
<th scope="col" style="width: 20%;padding: 0">
|
||||
|
|
Loading…
Reference in New Issue