现在位置: 首页 > Playwright 教程 > 正文

Playwright 自动等待与超时

本章深入介绍 Playwright 的自动等待机制,以及手动等待和超时配置的完整知识。


为什么需要自动等待

现代 Web 应用大量使用异步渲染,页面内容不会瞬间全部加载完毕。

传统测试框架中,开发者不得不写 sleepwaitFor 来等待,但这些操作不够灵活,要么等待太久拖慢测试,要么等待不够导致 flaky。

Playwright 将等待逻辑内置到每个 Locator 和 Action 中,从根本上解决这个问题。


三层自动等待体系

第一层:Locator 的自动等待

当你创建 Locator 时,Playwright 不立即查找元素,而是在你执行操作或断言时才开始查找并自动等待。

实例

// 创建 Locator(此时不会查找元素)
const btn = page.getByRole('button', { name: '延迟出现的按钮' });

// 点击时自动等待按钮出现(最多等待 action timeout 时间)
await btn.click();

第二层:Action 的可操作性检查

在执行操作(click、fill 等)前,Playwright 确保元素:Attached → Visible → Stable → Receives Events → Enabled。

第三层:Assertion 的自动重试

断言失败后不会立即报错,而是持续重试直到条件满足或超时。

实例

// 这个断言会不断重试 5 秒,直到文本出现或超时
await expect(page.getByText('数据加载完成')).toBeVisible();

手动等待方法

在自动等待不够用的特殊场景下,Playwright 也提供了手动等待方法。

page.waitForTimeout(ms) ——固定等待

实例

// 固定等待 2 秒(一般不推荐)
await page.waitForTimeout(2000);

尽量避免使用 waitForTimeout,固定等待在设备性能差异下极不稳定。优先使用后文的 waitForURLwaitForResponse 等事件驱动等待。

page.waitForURL() ——等待 URL 变化

实例

// 等待 URL 精确匹配
await page.waitForURL('https://www.runoob.com/dashboard');

// 等待 URL 包含特定模式
await page.waitForURL(/dashboard/);

page.waitForLoadState() ——等待加载状态

实例

// 等待 load 事件
await page.waitForLoadState('load');

// 等待 DOMContentLoaded
await page.waitForLoadState('domcontentloaded');

// 等待网络空闲
await page.waitForLoadState('networkidle');

page.waitForResponse() ——等待网络响应

实例

// 等待特定 API 响应
const response = await page.waitForResponse(
  resp => resp.url().includes('/api/data') && resp.status() === 200
);

// 获取响应数据
const data = await response.json();

page.waitForEvent() ——等待事件

实例

// 等待弹窗出现
const dialogPromise = page.waitForEvent('dialog');
await page.getByRole('button', { name: '删除' }).click();
const dialog = await dialogPromise;
await dialog.accept();

// 等待新页面打开
const pagePromise = page.context().waitForEvent('page');
await page.getByRole('link', { name: '新窗口打开' }).click();
const newPage = await pagePromise;

超时配置详解

Playwright 有 4 种超时类型,分别作用于不同层面:

超时类型配置文件位置默认值作用对象
Test timeouttimeout30000ms整个测试用例
Expect timeoutexpect.timeout5000ms断言重试
Action timeoutuse.actionTimeout无(不限)元素操作(click、fill 等)
Navigation timeoutuse.navigationTimeout无(不限)页面导航(goto、goBack 等)

全局配置

实例

// 文件路径:playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  // 每个测试的总超时时间(毫秒)
  timeout: 60000,

  // 断言自动重试的超时时间
  expect: {
    timeout: 10000,
  },

  use: {
    // 每个操作(click、fill 等)的超时时间
    actionTimeout: 15000,

    // 每次导航的超时时间
    navigationTimeout: 30000,
  },
});

单次设置(优先级高于全局配置)

实例

// 单个测试设置总超时
test.setTimeout(120000);

// 单个操作设置超时
await page.getByRole('button').click({ timeout: 30000 });

// 单次导航设置超时
await page.goto('https://www.runoob.com/', { timeout: 60000 });

超时优先级

单次设置 > 配置文件全局设置 > 默认值。


合理设置超时的建议

场景建议超时说明
本地开发默认值本地服务响应快,默认值足够
CI 环境2~3 倍默认值CI 机器性能波动大
慢速页面actionTimeout: 15000大表单、复杂页面
第三方服务调用test.setTimeout(120000)外部 API 响应慢