最佳实践与常见反模式
本教程共 59 篇 · 第 55 篇 · 更新于 2026-08-04 · 约 9 分钟阅读
55. 最佳实践与常见反模式
本节目标:学完你能避开 Playwright 测试里最典型的脆皮写法,写出抗改、好读、好维护的测试。
前面章节零散讲过一些原则,这章集中梳理:哪些是官方力推的 do,哪些是新手常踩的 don’t。照着做,测试不容易「昨天能跑今天挂」。
定位器:挑抗变的那个
用定位器定位元素优先用「用户看得见的属性」:getByRole、getByText、getByLabel,再不济用 data-testid。别一上来就 CSS 类或 XPath——设计师一动样式,测试就崩。
// 👍 抗变
page.getByRole('button', { name: 'submit' });
// 👎 脆弱
page.locator('button.buttonIcon.episode-actions-later');
Tip不确定用哪个?让
codegen帮你挑。它优先 role、text、testid,还能在多个匹配里自动收窄。
用链式和过滤缩小范围
Locator 可以串起来,慢慢缩到目标。比一长串 CSS 选择器好读也好改:
await page
.getByRole('listitem')
.filter({ hasText: 'Product 2' })
.getByRole('button', { name: 'Add to cart' })
.click();
断言:坚持 Web 优先
Web 优先断言(如 toBeVisible())会等条件满足、自动重试。手动断言不等人,容易误报。
// 👍 自动等
await expect(page.getByText('welcome')).toBeVisible();
// 👎 不等,当下就查
expect(await page.getByText('welcome').isVisible()).toBe(true);
Warning别把
await写进expect里面。上面第二种写法,isVisible()的结果是瞬间布尔值,断言不重试,元素晚半秒出现就挂。
测用户看得见的行为
测试该验证「用户最终看到/用到的」,别去测函数名、是不是数组、CSS 类名这类实现细节。用户只跟渲染出来的东西打交道,你的测试也该只看渲染结果。
测试之间要隔离
每个测试要独立:自己的存储、cookie、数据。隔离好了,可复现、好调试、不会一个挂了一串挂。
重复的登录步骤可以用 beforeEach 收口:
import { test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
await page.goto('https://github.com/login');
await page.getByLabel('Username or email address').fill('username');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
});
test('first', async ({ page }) => { /* 已登录 */ });
test('second', async ({ page }) => { /* 已登录 */ });
Note登录这种重活,更优解是「共享登录态」:用 setup project 登一次,其余测试复用,省时间。
别测你控制不了的东西
第三方站点、外部接口别直接测。内容你控制不了,还可能有 cookie 弹窗把你测试卡死。要测外部依赖的返回,用网络 API 自己造:
await page.route('**/api/fetch_data_third_party_dependency', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');
跨浏览器与 CI
在配置里一次配好多浏览器项目,覆盖 chromium、firefox、webkit,保证各用户都能用:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});
CI 上几条经验:
- 用 Linux,便宜。本地随意。
- 只装用得到的浏览器,比如只测 Chromium 就只装它。
- Trace 设
'on-first-retry',失败才录,省性能。 - 频繁跑,最好每次提交和 PR 都跑。
保持依赖新鲜
Playwright 月度发版。常更新,能趁新浏览器发布前抓出问题:
npm install -D @playwright/test@latest
查当前版本:npx playwright --version。配合 ESLint 的 no-floating-promises 规则,能逮出漏写的 await。
其他顺手的小建议
- 并行是默认的。同文件内测试顺序跑;想同文件并行,加
test.describe.configure({ mode: 'parallel' })。 - 软断言不立即中断,能一次列出多处不符:
await expect.soft(...).toHaveText('Success')。 - 用工具:VS Code 插件、codegen、Trace Viewer、UI Mode,都是官方给的省力工具,别硬扛。
一句话:抗变的定位器 + Web-First 断言 + 测试隔离,是 Playwright 测试稳的根。能走正门就别翻窗。
小结
这章把前面零散的原则拢到一起:定位器优先 getByRole、getByText,别一上来就 CSS 类;断言坚持 Web-First,自动重试不误报;测试之间要隔离,别让一个挂了一串挂。第三方站点别直接测,用 page.route 自己造返回。CI 上 Trace 设 on-first-retry,只录失败的,省性能。这三条线守住了——抗变定位器、Web-First 断言、测试隔离——测试就不容易「昨天能跑今天挂」。