如何利用Python编写高可用的UI自动化脚本_使用pytest驱动Playwright
Playwright测试失败主因是pytest未管理其生命周期,手动调用sync_playwright()易致进程残留;官方推荐使用pytest-playwright插件的page fixture,实现自动启停、上下文隔离与多浏览器支持。
为什么直接用 playwright.sync_api.sync_playwright 启动浏览器会失败?
因为 pytest 默认不管理 Playwright 的生命周期,手动调用 sync_playwright() 后没正确关闭浏览器或上下文,容易导致进程残留、端口占用、后续测试卡死。更关键的是,Playwright 官方明确建议:不要在测试函数里自己 with sync_playwright() as p: —— 这会绕过其内置的自动清理和复用机制。
实操建议:
- 弃用手动启动,改用 pytest-playwright 插件提供的
pagefixture(它已预配置好 Chromium/Firefox/WebKit 三端支持、自动启动/关闭、上下文隔离) - 安装时必须执行
pip install pytest-playwright,再运行playwright install chromium(否则pagefixture 会报BrowserType.connect_over_cdp: No browser found) - 若需指定浏览器,用命令行参数:
pytest --browser=firefox,而非在代码里硬编码p.firefox.launch()
如何让 UI 脚本真正“高可用”?重点不是重试,而是状态感知
单纯加 time.sleep(3) 或用 page.wait_for_timeout(5000) 是反模式。真实页面加载受网络、JS 执行、SPA 路由、动态渲染影响,固定等待既慢又不可靠。高可用的核心是:等“条件”,而不是等“时间”。
实操建议:
- 用
page.wait_for_selector("text=提交成功", state="visible")替代page.wait_for_timeout()—— 它会轮询直到元素出现且可见,超时才抛异常 - 对异步请求结果做断言时,拦截并等待 API 响应:
with page.expect_response(lambda r: "api/order" in r.url and r.status == 200): page.click("button#submit") - 避免用
page.query_selector()判断元素是否存在,改用page.locator("button[disabled]").count() == 0检查可交互态,更贴近用户真实操作路径
pytest + Playwright 下,如何稳定处理弹窗、iframe 和多标签页?
Playwright 的 page fixture 默认只绑定主页面,遇到新窗口、iframe 或 alert 会直接报 TimeoutError: Waiting for locator("...") failed,不是 selector 写错了,而是上下文没切对。
Taskade
高颜值AI大纲和思维导图生成,为你的团队建立第二个大脑。
下载
实操建议:
- 弹窗(new page):用
page.wait_for_event("popup")获取新页面对象,不能继续用原page;示例:with page.expect_popup() as popup_info: page.click("a[target='_blank']") new_page = popup_info.value new_page.wait_for_load_state("networkidle") - iframe:用
page.frame_locator("iframe[title='payment']")定位,再链式调用.get_by_role("button", name="确认"),别用page.frame("name")(已废弃) - alert:用
page.on("dialog", lambda dialog: dialog.accept())监听并自动处理,而不是等它阻塞后续执行
为什么 CI 环境里脚本总在 headless 模式下失败?
本地能跑通,CI(如 GitHub Actions)报 TimeoutError: Locator("button").click: Timeout 30000ms exceeded,大概率不是 selector 问题,而是 headless 浏览器缺少字体、GPU 加速或系统级依赖,导致页面渲染卡顿、JS 执行延迟、甚至某些 CSS 选择器失效(比如依赖 :hover 状态的按钮)。
实操建议:
- CI 中启用
--headful调试(仅限 debug 阶段),配合record_video_dir="videos/"查看真实渲染过程 - 给
pagefixture 加全局等待策略:page.set_default_timeout(10000)和page.set_default_navigation_timeout(15000),比单个操作加 timeout 更省事 - 禁用动画提升稳定性:
page.add_init_script("document.documentElement.style.animationDuration = '0s';")
最常被忽略的一点:Playwright 的 page fixture 在 pytest 中默认是 function-scoped,但如果你在 conftest.py 里错误地把它改成 class-scoped,多个 test 方法共用一个 page 实例,就会因状态污染(比如登录态、路由、缓存)导致偶发失败——高可用的前提,是每个 test 彼此隔离。