第 4 章 · 自动等待与 Actionability
本章目标:理解 Playwright 自动等待的原理与五项 actionability 检查,知道每个操作到底在等什么,以及 timeout / force 的正确用法。
4.1 自动等待解决的是什么问题
Selenium 时代的测试长这样:
# ❌ 反模式:猜一个"应该够久"的时间
time.sleep(3)
driver.find_element(By.ID, "submit").click()睡 3 秒:网络快时浪费 2.8 秒,网络慢时依然失败。Playwright 把等待内置进每个操作——执行动作前自动等所有相关检查通过,超时才报 TimeoutError。你的测试代码里不应该再出现任何 sleep。
4.2 五项 Actionability 检查
以 locator.click() 为例,执行前会依次确认:
| 检查项 | 含义 |
|---|---|
| Visible | 有非空边界框且无 visibility:hidden |
| Stable | 未处于动画中(连续两帧位置一致) |
| Receives Events | 命中测试证明该点没有被遮罩层挡住 |
| Enabled | 不是 disabled |
| Editable | (输入类)可编辑 |
不同操作的检查组合不同,官方文档给出了完整对照表(节选):
Action Visible Stable Receives Events Enabled Editable
locator.click() Yes Yes Yes Yes -
locator.check() Yes Yes Yes Yes -
locator.hover() Yes Yes Yes - -
locator.fill() Yes - - Yes Yes
locator.select_option() Yes - - Yes -
locator.set_input_files() - - - - -两个值得注意的细节:
fill不要求 Stable 和 Receives Events——往 input 里填文本不需要它静止或可点击;press、focus、dispatch_event几乎不做 actionability 检查——它们模拟的是键盘/事件层面的行为。
4.3 timeout:局部覆盖与全局默认
每次操作的默认超时是 30 秒,可按需调整:
# 单次操作放宽到 10 秒
page.get_by_role("button", name="提交").click(timeout=10_000)
# 全局默认改成 15 秒
page.set_default_timeout(15_000)
# 导航有独立的默认超时(45 秒),也可单独设置
page.set_default_navigation_timeout(60_000)不要用加长 timeout 来掩盖问题
timeout 是给"合理慢"的缓冲,不是失败重试器。一个稳定 5 秒内完成的页面配 120 秒超时,只会让 CI 失败时多等两分钟。
4.4 force=True:绕过检查的代价与用途
force=True 会跳过 actionability 检查直接派发动作:
# 即使元素被动画遮挡也强行点击
page.get_by_role("button").click(force=True)适用场景很窄:自定义动画导致 Receives Events 永远不通过、或确知遮挡元素不拦截事件时的一把"手术刀"。代价是放弃了 Playwright 最重要的安全保障——能用则不用,用了请在代码注释里写明原因。
4.5 等待的另一半:显式状态等待
自动等待管的是"操作前等元素就绪",但有些场景需要你主动声明等待条件:
# 等 URL 变化(如登录跳转)
page.wait_for_url("**/dashboard")
# 等特定网络响应到达
with page.expect_response("**/api/user") as resp_info:
page.get_by_role("button", name="刷新").click()
print(resp_info.value.status)
# 等某个请求发出
with page.expect_request("**/analytics") as req_info:
page.goto("https://example.com")
# 等 load/networkidle 状态
page.wait_for_load_state("networkidle")这些"事件等待器"同样是自动重试语义:在超时窗口内持续探测,而不是只查一次就走。
4.6 本章小结
- 每个动作前自动执行 actionability 检查,全部通过才执行,超时报 TimeoutError;
- 五大检查:可见(Visible)、稳定(Stable)、接收事件(Receives Events)、可用(Enabled)、可编辑(Editable),不同动作组合的检查项不同;
- 测试代码中禁止
sleep;timeout 只做局部微调,别全局调大掩盖问题; force=True跳过检查是最后手段;- 页面级变化用
wait_for_url/expect_response等显式等待表达意图。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 对一个带入场动画的按钮执行 click(),Playwright 会?
2. 关于 locator.fill() 的 actionability 检查,正确的是?
3. 测试里大量出现 time.sleep(2) 的最大危害是?
4. 什么时候适合使用 force=True?
🛠️ 动手实践
- 写一个含 CSS 加载动画按钮的本地页面,分别在动画进行中和结束后点击,观察 Playwright 自动等待的行为。
- 用
expect_response捕获一次真实站点的 XHR 请求,打印其状态码。 - 故意把 timeout 设为 1ms 点击一个延迟出现的按钮,观察 TimeoutError 报错信息里的检查细节。