使用@pytest.hookimpl(tryfirst=True)触发AttributeError问题求助
问题分析与解决
错误原因
你遇到的AttributeError: generator object has no attribute 'skipped',核心原因是**@pytest.hookimpl装饰的函数不能是生成器(不能使用yield)**:
- 原代码用
@pytest.mark.hookwrapper时,函数允许以生成器形式存在,通过yield获取测试执行的outcome对象,再通过outcome.get_result()拿到真实的TestReport实例,此时report是测试报告对象,具备skipped等属性。 - 改用
@pytest.hookimpl(tryfirst=True)后,你保留了yield语句,导致函数返回的是生成器对象,而非预期的测试报告对象,自然没有skipped属性。
解决方案
方案1:继续使用hookwrapper(推荐)
如果你的需求是拦截并修改测试报告(比如嵌入截图),@pytest.mark.hookwrapper是pytest专门设计用于这类场景的装饰器,完全适配你的现有逻辑,不需要更换装饰器。原代码本身是可行的,只需保留原装饰器即可:
@pytest.mark.hookwrapper def pytest_runtest_makereport(item): """ Extends the PyTest Plugin to take and embed screenshot in html report, whenever test fails. :param item: """ pytest_html = item.config.pluginmanager.getplugin('html') outcome = yield report = outcome.get_result() extra = getattr(report, 'extra', []) if report.when == 'call' or report.when == "setup": xfail = hasattr(report, 'wasxfail') if (report.skipped and xfail) or (report.failed and not xfail): clean_nodeid = report.nodeid.replace("tests/", "") file_name = ("./reports/screenshots/" + clean_nodeid.replace("::", "_") + ".png") driver.get_screenshot_as_file(file_name) screenshot = driver.get_screenshot_as_base64() extra.append(pytest_html.extras.image(screenshot, "")) report.extra = extra
方案2:改用@pytest.hookimpl(需重构代码)
如果一定要使用@pytest.hookimpl,需要调整函数结构,遵循该装饰器的要求:
- 函数不能是生成器,需去掉
yield - 函数签名要符合
pytest_runtest_makereport的标准:接收item和call两个参数 - 生成或修改测试报告后返回
示例代码如下:
@pytest.hookimpl(tryfirst=True) def pytest_runtest_makereport(item, call): pytest_html = item.config.pluginmanager.getplugin('html') # 获取原始测试报告 report = item.config.hook.pytest_runtest_makereport(item=item, call=call) extra = getattr(report, 'extra', []) if report.when == 'call' or report.when == "setup": xfail = hasattr(report, 'wasxfail') if (report.skipped and xfail) or (report.failed and not xfail): clean_nodeid = report.nodeid.replace("tests/", "") file_name = ("./reports/screenshots/" + clean_nodeid.replace("::", "_") + ".png") driver.get_screenshot_as_file(file_name) screenshot = driver.get_screenshot_as_base64() extra.append(pytest_html.extras.image(screenshot, "")) report.extra = extra return report
注意:这种方式需要确保你正确调用原始的hook获取报告,部分场景下可能会出现hook执行顺序的问题,因此更推荐方案1。
内容的提问来源于stack exchange,提问作者Luis
相关产品推荐
相关产品推荐

