pytest-selenium失败时需通过fixture中addfinalizer在teardown阶段检查request.node.rep_call.failed并调用driver.save_screenshot()实现自动截图,路径建议含时间戳和用例名;官方已移除screenshot_on_failure参数,须手动实现。

pytest-selenium 失败时怎么自动截图
默认情况下 pytest-selenium 不会自动截图,必须显式调用 driver.save_screenshot()。要实现「失败自动截图」,得靠 pytest 的钩子函数,在用例结束且状态为失败时触发保存逻辑。
关键点是使用 pytest_runtest_makereport 钩子获取测试结果,并在 teardown 阶段(或 exception 时)调用截图。但注意:此时 driver 实例可能已被 quit() —— 所以截图动作必须放在 driver 关闭前,通常绑定到 request.addfinalizer 或 fixture 的 teardown 中。
- 推荐把 driver 初始化写在 fixture 里,用
scope="function"确保每个用例独立 - 在 fixture 的 teardown 部分检查是否失败(通过
request.node.rep_call.failed),再截图 - 截图路径建议带时间戳和用例名,避免覆盖:
f"screenshots/{request.node.name}_{int(time.time())}.png"
如何在 fixture 中安全访问测试结果
pytest 的 request.node 在测试执行完后才填充 rep_call 属性,所以不能在 fixture setup 阶段读,而要在 finalizer 里延迟读取。常见错误是直接在 fixture 返回 driver 前就尝试读 rep_call,此时它还是 None。
正确做法是用 request.addfinalizer() 注册一个清理函数,该函数在测试函数执行完毕后运行,此时 request.node.rep_call 已就绪:
@pytest.fixture
def driver(request):
driver = webdriver.Chrome()
def fin():
if request.node.rep_call and request.node.rep_call.failed:
timestamp = int(time.time())
name = request.node.name.replace("[", "_").replace("]", "_")
driver.save_screenshot(f"screenshots/{name}_{timestamp}.png")
driver.quit()
request.addfinalizer(fin)
return driver
注意:rep_call 只对 call 阶段(即测试函数本身)有效;setup/teardown 失败会写入 rep_setup 或 rep_teardown,需分别判断。
pytest-selenium 自带的 screenshot_on_failure 参数有用吗
没用。pytest-selenium 早期版本曾提供 --screenshot-on-failure 命令行参数,但自 2.0+ 起已移除,文档也未保留。当前 PyPI 上最新版(如 4.x)完全不支持该参数,传了也不会生效,还会被 pytest 忽略或报错 unrecognized arguments。
- 不要依赖
--screenshot-on-failure或screenshot_on_failure=True这类配置 - 官方推荐方式就是手动 hook + fixture 控制,灵活性更高
- 如果项目用了
pytest-base-url或pytest-html,可配合生成带截图链接的报告,但截图动作仍需自己写
日志怎么同步保存(非截图)
浏览器控制台日志(console logs)、网络请求(performance logs)、Selenium 日志都不能靠截图捕获,得主动拉取。失败时最实用的是 driver.get_log("browser"),但要注意:logging_prefs 必须在 driver 初始化时开启,否则返回空列表。
示例(在 fixture 初始化 driver 时):
options = webdriver.ChromeOptions()
options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
driver = webdriver.Chrome(options=options)
然后在 finalizer 的失败分支中追加:
if request.node.rep_call.failed:
logs = driver.get_log("browser")
with open(f"logs/{name}_{timestamp}.log", "w") as f:
for entry in logs:
f.write(f"[{entry['level']}] {entry['timestamp']}: {entry['message']}\n")
容易忽略的是:不同浏览器日志类型不同("browser" 是 Chrome/Firefox 支持的,Edge 需用 "performance" 或 "driver"),而且 get_log() 在某些 Selenium 版本中对 headless 模式支持不稳定。










