E2E测试指南

Playwright vs Cypress 对比

特性PlaywrightCypress
浏览器支持Chromium、Firefox、WebKit(Safari)Chromium、Firefox、Electron
多标签/多窗口支持有限支持
语言JS/TS、Python、Java、C#仅 JS/TS
并行执行内置分片需要 Cypress Cloud(付费)
组件测试实验性成熟(@cypress/react)
最适合跨浏览器测试、复杂交互流程React/Vue/Angular 组件测试

Playwright:页面对象模型

// pages/LoginPage.ts import { Page, Locator } from '@playwright/test'; export class LoginPage { readonly emailInput: Locator; readonly passwordInput: Locator; readonly submitButton: Locator; constructor(private page: Page) { this.emailInput = page.getByLabel('邮箱'); this.passwordInput = page.getByLabel('密码'); this.submitButton = page.getByRole('button', { name: '登录' }); } async goto() { await this.page.goto('/login'); } async login(email: string, password: string) { await this.emailInput.fill(email); await this.passwordInput.fill(password); await this.submitButton.click(); } } // tests/login.spec.ts test('登录成功后跳转到仪表板', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.goto(); await loginPage.login('user@example.com', 'correct123'); await expect(page).toHaveURL('/dashboard'); await expect(page.getByText('欢迎回来')).toBeVisible(); });

避免不稳定测试

反模式修复方案
硬编码 sleep(1000)使用 await expect(locator).toBeVisible() — 自动等待
用 CSS 类名选择元素使用 data-testid 或 ARIA role — 稳定选择器
测试顺序依赖每个测试自行准备状态,完成后清理
调用真实第三方接口用路由拦截 Mock/Stub 外部 API
共享测试数据每个测试创建唯一数据(时间戳、随机 ID)
滚动/动画时序问题测试环境禁用动画(prefers-reduced-motion)