You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何捕获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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:16:26