共计 2115 个字符,预计需要花费 6 分钟才能阅读完成。
痛点分析:为什么传统工具在动态 Web 时代力不从心
传统自动化测试工具(如 Selenium)在应对现代 Web 应用时经常遇到以下问题:

- 元素定位不稳定:动态 ID、异步加载导致传统的 XPath/CSS 定位器频繁失效
- 等待机制笨拙 :强制使用 Thread.sleep() 或显式等待,既低效又不可靠
- 浏览器兼容成本高:需要额外驱动配置,不同浏览器行为差异大
- 执行速度慢:基于 WebDriver 的架构带来额外通信开销
技术对比:Playwright 的降维打击
| 特性 | Selenium | Cypress | Playwright |
|---|---|---|---|
| 自动等待机制 | 需要显式配置 | 部分支持 | 内置智能等待(推荐) |
| 执行速度 | 慢(HTTP 协议) | 较快 | 极快(WebSocket 直连) |
| 多标签页支持 | 复杂 | 不支持 | 原生支持 |
| 浏览器支持 | 需单独驱动 | Chromium 系 | 统一 API 支持三大引擎 |
核心实现:编写健壮定位器的艺术
1. 定位策略选择金字塔(从稳定到脆弱)
// 最佳实践:按优先级选择定位器
const locatorStrategies = {role: page.getByRole('button', { name: 'Submit'}), // 语义化首选
text: page.getByText('Welcome back'), // 可见文本
testId: page.getByTestId('login-form'), // 专用测试属性
label: page.getByLabel('Username'),
css: page.locator('.primary-btn'), // 谨慎使用
xpath: page.locator('//button[contains(@class,"submit")]') // 最后手段
};
2. 自动等待的魔法
Playwright 的 auto-waiting 机制会自动处理:
- 元素是否存在 DOM 中
- 是否可见(非隐藏 / 透明)
- 是否稳定(停止动画)
- 是否可交互(未被遮挡)
// 不需要手动等待!以下操作会自动等待元素就绪
await page.getByRole('button').click();
await page.getByLabel('Search').fill('query');
高级技巧:攻克复杂场景
1. Shadow DOM 穿透
# 方法 1:直接穿透 shadow 边界
await page.locator('custom-component::shadow-dom(=input)').click()
# 方法 2:先定位宿主再深入
component = page.locator('custom-component')
await component.evaluate('elem => elem.shadowRoot.querySelector("input")')
2. 跨浏览器测试配置
// playwright.config.js
module.exports = {
projects: [{ name: 'Chromium', use: { browserName: 'chromium'} },
{
name: 'Firefox',
use: {
browserName: 'firefox',
launchOptions: {firefoxUserPrefs: { 'dom.events.asyncClipboard': true} }
}
},
{name: 'WebKit', use: { browserName: 'webkit'} }
]
};
性能优化:从分钟级到秒级
1. 测试并行化
# 启动 3 个 worker 并行执行
npx playwright test --workers=3
2. 资源录制策略
| 配置项 | 内存占用 | 执行时间影响 | 适用场景 |
|---|---|---|---|
| video: ‘on’ | 高 | +15% | 调试失败用例 |
| screenshot: ‘on’ | 中 | +5% | CI 环境验证 |
| trace: ‘retain’ | 低 | <1% | 生产环境监控 |
三大避坑指南
-
避免使用 sleep
❌await page.waitForTimeout(5000)
✅ 使用事件驱动等待:await expect(page.getByText('Data loaded')).toBeVisible(); -
不要过度依赖 CSS 类名
❌.login-form > div:nth-child(3) button
✅ 使用语义化查询:page.get_by_role("button", name="Sign in") -
谨慎处理 iframe
❌ 直接在主页面定位 iframe 元素
✅ 先切换到 iframe 上下文:Frame frame = page.frame("payment-form"); frame.locator("#card-number").fill("4111111111111111");
延伸思考
- 如何设计定位策略的 fallback 机制?例如当首选定位器失效时,自动尝试备用方案
- 在微前端架构下,如何构建跨应用的统一测试策略?
实战心得
经过半年在生产环境的实践,我们的测试稳定性从 78% 提升到 99.5%。关键收获:
- 优先使用
getByRole()和getByText()等语义化查询 - 利用
expect().toHaveScreenshot()进行视觉回归测试 - 在 CI 中启用
--retries=2自动重试机制
(注:所有代码示例已在 Playwright 1.35+ 版本验证通过)
正文完
发表至: 自动化测试
近一天内
