是否存在在上下文管理器__exit__前执行的pytest异常处理钩子?
解决方案:在上下文管理器__exit__前捕获异常并截图
好问题!我之前做自动化测试时也碰到过一模一样的场景——用上下文管理器管理会话(比如浏览器会话),想在测试崩掉时先截图再让会话自动关闭,试了pytest_runtest_teardown和pytest_runtest_makereport确实都太晚了,它们都是在上下文退出后才触发的。给你分享几个靠谱的解决思路:
1. 在Fixture里包裹会话(最推荐)
如果你的会话是通过pytest Fixture提供的,直接在Fixture的try-except块里处理异常就行,这是最贴合pytest生态的方式:
import pytest from your_session_module import YourSession @pytest.fixture(scope="function") def test_session(): session = YourSession() try: yield session # 这里把会话交给测试用例 except Exception: # 测试抛出异常时,先执行截图,此时会话还没关闭 take_screenshot(session) raise # 必须重新抛出异常,不然pytest会误以为测试通过 finally: # 不管有没有异常,最后关闭会话(等价于上下文管理器的__exit__逻辑) session.close()
测试用例里直接调用这个fixture即可,异常发生时,截图逻辑会在会话关闭前优先执行。
2. 自定义上下文管理器包装原会话
如果你的测试是直接用with语句创建会话,那可以自己写一个上下文管理器把原会话包起来,把截图逻辑嵌入进去:
from contextlib import contextmanager from your_session_module import YourSession @contextmanager def session_with_screenshot(): session = YourSession() try: yield session except Exception: take_screenshot(session) raise # 不要吞掉异常,让pytest正常捕获并标记测试失败 finally: session.close()
测试里替换成如下写法即可:
def test_something(): with session_with_screenshot() as session: # 执行测试逻辑 session.do_something()
这样异常触发时,会先执行截图操作,再进入finally块关闭会话,完全符合你的需求。
3. 用底层钩子pytest_runtest_protocol(谨慎使用)
这个钩子是pytest执行测试用例的最外层入口,时机非常早,但因为太底层,容易和pytest的其他功能(比如参数化、fixture依赖)冲突,只推荐在特殊场景下尝试:
def pytest_runtest_protocol(item, nextitem): try: # 执行测试用例的全流程(包括fixture初始化、测试执行) item.runtest() except Exception: take_screenshot() raise return True # 告诉pytest我们已自行处理测试执行流程
这个方式的局限性在于,如果你的会话是通过fixture创建的,item.runtest()已经包含了fixture的teardown逻辑,所以可能还是要结合fixture的方式配合使用,不如前两种方案直接稳妥。
总的来说,前两种方式更实用,尤其是Fixture的方案,既能享受到pytest的fixture生命周期管理,又能完美控制截图时机。
内容的提问来源于stack exchange,提问作者president
相关产品推荐
相关产品推荐

