Playwright Python 教程:中级篇
Playwright Python 中级教程摘要 本教程重点讲解如何提升Playwright脚本的稳定性和效率,涵盖五大核心内容: 高级等待策略:避免使用固定等待,推荐自动等待和显式等待(wait_for_selector、wait_for_url等) 复杂UI组件处理:包括下拉选择框、文件上传和对话框的专用API Frame/Iframe操作:通过content_frame切换上下文,支持nam
文章目录
Playwright Python 教程:中级篇
目标
本篇将帮助你从"能用"到"用好",重点讲解如何处理真实测试场景中的复杂情况,让你的脚本更加稳定和高效。
一、高级等待策略:告别 time.sleep
在基础篇中,我们提到了要避免使用 page.wait_for_timeout()
。这是因为固定等待时间非常不可靠(网络或机器速度可能导致元素加载或快或慢)。Playwright 提供了内置的自动等待和更智能的显式等待机制。
1. 内置的自动等待(Auto-waiting)
Playwright 的核心优势。在执行操作(如 click
, fill
)之前,它会自动执行一系列可操作性检查:
- 元素是否存在(Attached)
- 元素是否可见(Visible)
- 元素是否稳定(Not animated, Stable)
- 元素是否可交互(Enabled, Editable)
- 元素是否接收到事件(Not obscured)
这意味着你通常不需要手动等待元素出现,直接操作即可。page.click(selector)
会一直等待该元素可点击(最多 30s
,超时则报错)。
2. 显式等待(Explicit Waits)
有些情况下,你需要等待一个特定的条件发生,而不是某个元素可操作。这时就需要显式等待。
-
page.wait_for_selector(selector, state="visible", timeout=30000)
: 等待匹配选择器的元素达到特定状态(如"visible"
,"hidden"
,"attached"
,"detached"
)。# 等待一个加载 spinner 消失 page.wait_for_selector(".spinner", state="hidden") # 等待成功消息出现 page.wait_for_selector("text=Operation successful", state="visible")
-
page.wait_for_url(url, timeout=30000)
: 等待页面导航到指定的 URL(支持通配符和正则表达式)。# 等待页面跳转到 dashboard 页 page.wait_for_url("**/dashboard")
-
page.wait_for_function(js_function, arg=None, timeout=30000)
: 等待页面中的 JavaScript 函数返回真值。这是最灵活的等待方式。# 等待 window.isDataReady 变量变为 true page.wait_for_function("window.isDataReady === true") # 等待文档的高度超过 1000px page.wait_for_function("() => document.body.scrollHeight > 1000")
-
page.wait_for_timeout(milliseconds)
: 最后的手段。只有在没有其他更好方法时(例如等待一个非前端的后端处理),才使用固定等待。
二、处理复杂的 UI 组件
1. 下拉选择框(Select Dropdowns)
Playwright 为 <select>
元素提供了专用的 API,非常方便。
# 导航到有下拉框的页面,例如 https://demoqa.com/select-menu
page.goto("https://demoqa.com/select-menu")
# 通过标签选择 'Green'
page.select_option("#oldSelectMenu", "green")
# 通过值(value)选择 'Blue'
page.select_option("#oldSelectMenu", "blue")
# 或者通过索引选择 (索引从0开始)
page.select_option("#oldSelectMenu", index=3)
2. 文件上传(File Uploads)
处理 <input type="file">
元素非常简单。
# 设置文件路径,一次选择一个文件
page.set_input_files('input[type="file"]', "path/to/myfile.pdf")
# 一次选择多个文件
page.set_input_files('input[type="file"]', ["file1.pdf", "file2.jpg"])
# 清空选择(将 input 的值设为空)
page.set_input_files('input[type="file"]', [])
3. 处理对话框(Dialogs: Alert, Confirm, Prompt)
Playwright 可以监听并响应 JavaScript 的原生对话框。
# 在点击触发对话框的按钮之前,先注册一个监听器
page.once("dialog", lambda dialog: dialog.accept()) # 默认点击 "OK"
# 或者,也可以获取对话框消息并做出选择
def handle_dialog(dialog):
print(f"对话框消息: {dialog.message}")
if "确认删除" in dialog.message:
dialog.accept() # 点击 "OK" 或 "Confirm"
else:
dialog.dismiss() # 点击 "Cancel"
page.on("dialog", handle_dialog)
# 现在执行会触发对话框的操作,例如点击删除按钮
page.click("#delete-button")
# 操作完成后,最好移除监听器,以免影响后续操作
page.remove_listener("dialog", handle_dialog)
三、Frame 和 Iframe 处理
网页中的 iframe 就像一个"页面中的页面"。要操作 iframe 内的元素,必须先切换到对应的 frame
对象上。
1. 通过 Name/URL 或选择器定位 Frame
# 通过 frame 的 name 属性或 URL 匹配
frame = page.frame(name="my-frame")
frame = page.frame(url="**/login.html")
# 通过 CSS/XPath 选择器定位 iframe 元素,然后获取其对应的 frame 对象
iframe_element = page.query_selector(".my-iframe")
frame = iframe_element.content_frame
2. 在 Frame 内操作元素
获取到 frame
对象后,其 API 和 page
对象几乎一样。
# 在 frame 内点击、填写
frame.click("button")
frame.fill("#username", "myuser")
# 也可以使用 with 语句进行上下文切换,这是一种更清晰的方式
with page.frame("my-frame"):
page.click("button") # 这里的 page 操作会自动限定在 frame 内
四、管理多个 Page 和 Tab
点击一个链接有时会打开新标签页或新窗口。Playwright 可以轻松管理多个页面。
# 在点击之前,监听新的 page 事件
with page.expect_popup() as popup_info:
page.click('a[target="_blank"]') # 点击一个会打开新窗口的链接
# 获取新打开的 page 对象
new_page = popup_info.value
# 等待新页面加载完成
new_page.wait_for_load_state()
# 在新页面上进行操作
print(new_page.title())
# 操作完成后,关闭新页面
new_page.close()
# 切回原始页面继续操作
page.bring_to_front() # 将原始页面提到最前(激活状态)
page.click("#back-to-original")
五、实战演练:一个综合案例
让我们模拟一个稍复杂的场景:登录一个需要处理 iframe 和等待的网站。
from playwright.sync_api import sync_playwright
def test_complex_login():
with sync_playwright() as p:
# 启动浏览器,设置视口大小
browser = p.chromium.launch(headless=False)
context = browser.new_context(viewport={"width": 1920, "height": 1080})
page = context.new_page()
# 1. 导航到登录页
page.goto("https://example.com/login")
# 2. 处理登录表单(假设它在 iframe 里)
login_frame = page.frame(url="**/login-iframe")
assert login_frame, "Login iframe not found"
# 在 iframe 内操作
login_frame.fill("#username", "testuser")
login_frame.fill("#password", "testpass")
# 3. 点击登录按钮,等待导航到仪表盘(Dashboard)
with page.expect_navigation(): # 等待主页面发生导航
login_frame.click("#submit-btn")
# 4. 等待仪表盘上的某个关键元素出现
page.wait_for_selector("text=Welcome, testuser", state="visible")
page.wait_for_url("**/dashboard")
# 5. 进行一些操作,例如上传头像
# 假设点击一个按钮会触发文件选择对话框
page.click("#upload-avatar")
page.set_input_files('input[type="file"]', "avatar.jpg")
# 6. 等待上传成功的 Toast 消息出现并消失
page.wait_for_selector("text=Upload successful", state="visible")
page.wait_for_selector("text=Upload successful", state="hidden")
# 7. 断言头像图片的 src 属性已更新
avatar_src = page.get_attribute("#user-avatar", "src")
assert avatar_src and "avatar" in avatar_src, "Avatar was not updated"
print("✅ 综合测试通过!")
browser.close()
if __name__ == "__main__":
test_complex_login()
中级篇总结
你现在已经掌握了处理真实世界 Web 应用自动化测试的中级技能:
- 智能等待:使用
wait_for_selector
,wait_for_function
等替代固定等待,让脚本更稳定。 - 复杂组件交互:熟练处理下拉框、文件上传和JavaScript 对话框。
- Frame 处理:能够定位
frame
对象并在其上下文内进行操作。 - 多页面管理:使用
expect_popup
来捕获和控制新打开的标签页。
下一步(高级篇预览):
在高级篇,我们将探索真正强大的功能,迈向专家水平:
- 网络拦截与模拟:mock API 响应,修改请求头,实现"无依赖测试"。
- 认证与持久化状态:如何保存登录状态,实现一次登录多次测试。
- 高级浏览器配置:代理设置、语言、地理位置模拟。
- 录制视频与追踪:生成测试执行的视频和可视化报告。
- 与 Pytest 集成:如何组织测试用例、夹具(fixtures)和生成报告。
更多推荐
所有评论(0)