如何捕获conftest.py中抛出的异常?Pytest插件异常处理需求
解决方案
方案1:改进Subprocess启动脚本(捕获具体异常)
你之前的Subprocess方案仅通过返回码判断状态,要捕获具体异常类型,需解析pytest的输出内容。以下是优化后的脚本:
import subprocess import traceback def run_pytest_and_report(params=None): params = params or [] try: # 启动pytest,捕获标准输出/错误 popen = subprocess.Popen( ['pytest'] + params, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=False # 禁用shell=True,避免安全风险 ) # 实时读取并保存输出 output_lines = [] for line in iter(popen.stdout.readline, ''): output_lines.append(line) print(line, end='') # 可选:实时打印测试日志 return_code = popen.wait() full_output = ''.join(output_lines) if return_code != 0: # 从输出中解析异常类型 error_type = None traceback_started = False for line in output_lines: if line.startswith('Traceback'): traceback_started = True elif traceback_started and (':' in line and ('Error' in line or 'Exception' in line)): error_type = line.split(':')[0].strip() break # 生成上报内容 if error_type: reason = f'测试执行出错: {error_type}' else: reason = '测试执行出错(未知异常类型)' make_http_report(reason=reason, detail=full_output) else: make_http_report(reason='测试执行完成,全部通过') except Exception as e: # 捕获启动pytest时的异常 make_http_report( reason=f'启动pytest失败: {str(e)}', detail=traceback.format_exc() ) if __name__ == '__main__': run_pytest_and_report(['-xvs', 'tests/']) # 替换为你的测试参数
该脚本会完整捕获pytest的输出日志,解析异常类型后上报,同时处理pytest启动失败的场景。
方案2:插件内全局异常捕获+早期钩子
如果不想依赖外部脚本,可在插件中通过全局异常捕获+早期钩子覆盖conftest加载阶段的错误:
import pytest import sys import traceback # 全局未捕获异常处理:覆盖conftest加载时的崩溃场景 def _uncaught_exception_handler(exc_type, exc_value, exc_traceback): if issubclass(exc_type, Exception): make_http_report( reason=f'插件加载前出现未捕获错误: {exc_type.__name__}: {exc_value}', detail=''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)) ) # 保留系统默认异常处理逻辑 sys.__excepthook__(exc_type, exc_value, exc_traceback) sys.excepthook = _uncaught_exception_handler # 早期钩子:在加载conftest前触发测试启动上报 @pytest.hookimpl(tryfirst=True) def pytest_load_initial_conftests(early_config, parser, args): try: make_test_start_report_request(xxxxx) # 替换为你的上报逻辑 except Exception: # 确保上报失败不中断测试流程 pass @pytest.hookimpl(hookwrapper=True, trylast=True) def pytest_sessionfinish(session, exitstatus): yield try: if exitstatus != 0: make_test_end_report_request(f'测试结束,退出码: {exitstatus}') else: make_test_end_report_request('测试结束,全部通过') except Exception: pass @pytest.hookimpl(tryfirst=True) def pytest_sessionstart(session): # 作为pytest_load_initial_conftests的兜底 try: make_test_start_report_request(xxxxx) except Exception: pass
通过sys.excepthook捕获所有主线程未处理异常,包括conftest加载时的错误;pytest_load_initial_conftests是pytest最早执行的钩子之一,能确保测试启动上报在conftest加载前触发。
方案3:应急方案——跳过conftest加载
如果同事的conftest错误频发,可临时通过--no-conftest参数跳过所有conftest文件,再在插件中自行加载必要配置:
pytest --no-conftest
注意:此方案会影响依赖conftest的测试用例,仅适合应急场景。
关键注意事项
- 所有上报函数(如
make_http_report)需内部做异常捕获,避免上报失败导致二次崩溃 - Subprocess方案中禁用
shell=True,避免命令注入风险,同时确保pytest路径在系统环境变量中 - 若测试代码涉及多线程,需额外处理线程的未捕获异常
内容的提问来源于stack exchange,提问作者Chweng Mega
相关产品推荐
相关产品推荐

