pytest.raises必须在with块内使用,直接调用无效;校验异常消息推荐用"in"判断;Playwright的TimeoutError需显式导入;全局异常需通过sys.excepthook等多层兜底。

pytest.raises 必须放在 with 块内执行
直接调用 pytest.raises(ValueError) 不会捕获异常,它只是返回一个上下文管理器对象。异常必须在 with 块内部实际抛出,否则测试会静默通过,掩盖真实问题。
- 错误写法:
func_that_raises(); pytest.raises(ValueError)—— 异常已在块外抛出并中断执行,pytest.raises根本没机会生效 - 正确写法:
with pytest.raises(ValueError) as exc_info: func_that_raises() - 若函数有返回值且你关心其状态,不能依赖
pytest.raises吞掉后的“无返回”来推断——它不提供返回值访问能力 - 校验异常消息推荐用
"timeout" in str(exc_info.value),比正则match更稳定,避免空格、换行或版本差异导致误失败
Playwright 中要区分 TimeoutError 和普通 Exception
TimeoutError 是 Playwright 自定义异常(非内置 builtins.TimeoutError),直接 except Exception: 虽能捕获,但会丢失语义精度;而裸写 except: 又可能吞掉 KeyboardInterrupt,破坏 Ctrl+C 中断能力。
- 应显式捕获
from playwright.sync_api import TimeoutError或from playwright.async_api import TimeoutError - 不要混用
builtins.TimeoutError,二者类型不同,isinstance(e, TimeoutError)会返回False - 若需统一处理多种 Playwright 异常(如
TimeoutError、ElementHandleError),建议用元组:except (TimeoutError, ElementHandleError) as e: - 异步场景下,
async with不支持pytest.raises,得改用try/except+assert组合
全局未捕获异常不能靠 except Exception: 拦截
sys.excepthook 才是真正兜住主线程未处理异常的最后机制;except Exception: 只作用于当前 try 块,对顶层崩溃无效。
- 设置方式:
import sys; sys.excepthook = lambda t, v, tb: logging.critical("Crash", exc_info=(t, v, tb)) - 注意:该钩子不生效于子线程——每个线程需单独设
threading.excepthandler,或在线程入口包一层try/except - 异步任务中,
asyncio需用loop.set_exception_handler(),而非sys.excepthook - Flask/FastAPI 等 Web 框架中,
@app.errorhandler(Exception)只捕获 HTTP 请求生命周期内的异常,无法覆盖后台定时任务或信号处理中的崩溃
自定义异常类要继承 Exception 而非 BaseException
继承 BaseException 会让异常被 except Exception: 漏掉,且可能意外拦截 SystemExit 或 KeyboardInterrupt,导致测试无法手动终止。
立即学习“Python免费学习笔记(深入)”;
- 正确写法:
class ElementNotFound(Exception): pass - 错误写法:
class ElementNotFound(BaseException): pass—— 这样pytest.raises(ElementNotFound)仍能工作,但except Exception:就捕获不到 - 若需在日志中保留原始 traceback,别直接 print(e),用
traceback.format_exception(type(e), e, e.__traceback__)获取完整字符串 - 生产环境不要在
except Exception:块里只写pass或空print,至少调用logging.exception()记录堆栈


















