关于 playwright-e2e-testing
playwright-e2e-testing 是用于编写和维护 Playwright 网页端到端测试的 TypeScript 技能,涵盖 UI 流程、API 辅助设置、响应式验证、模拟和调试。
主要功能
- 01编写 TypeScript 端到端测试
- 02测试表单、导航和身份验证
- 03模拟并拦截 API 请求
- 04检查响应式布局和视觉变化
- 05使用追踪、截图和视频调试
适用场景
- 01验证登录或结账流程
- 02使用模拟 API 响应测试 UI
- 03检查移动端和桌面端布局
- 04排查不稳定的浏览器测试
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ fugazi/ test-automation-skills-agents --skill playwright-e2e-testing安装后在 Claude Code 中输入/playwright-e2e-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.txtreferences/debugging-errors-network.mddebugging-flaky-locators.mddebugging-tools-ui.mddebugging-tracing-logging.mdfile-map-template.mdlocator-strategies-anti-debug.mdlocator-strategies-filtering.mdlocator-strategies-priority.mdlocator-strategies-text.mdpage-object-model-basics.mdpage-object-model-components.mdpage-object-model-fixtures.mdpage-object-model-practices.mdsnippets-assertions-debugging.mdsnippets-interactions.mdsnippets-setup.mdsnippets-viewports-auth.md详细介绍
Playwright E2E 测试(TypeScript)
使用 Playwright 和 TypeScript 对 Web 应用进行端到端测试的综合工具包。遵循最佳实践,支持可靠的 UI 测试、依赖 UI 的 API 设置以及响应式设计验证。
激活条件: 在编写或维护版本化的 Playwright UI 规范及其测试基础设施时触发此技能。
适用场景
- 编写 E2E 测试,覆盖用户流程、表单、导航和身份验证
- 通过
requestfixture 或网络拦截进行依赖 UI 的 API 设置 - 响应式测试,覆盖移动设备、平板和桌面视口
- 使用跟踪记录、屏幕截图、视频和 Playwright Inspector 调试不稳定的测试
- 使用 Page Object Model 和 fixture 设置测试基础设施
- 模拟/拦截 API,进行隔离且确定性的测试
- 使用屏幕截图对比进行视觉回归测试
不适用场景
- 不涉及浏览器的独立 API/契约测试(使用
api-testing)。 - 以交互方式驱动真实浏览器进行探索或调试(使用
playwright-cli)。 - 管理大型回归测试套件、测试分层或 CI 分片策略(使用
playwright-regression-testing)。 - Selenium/Java 浏览器自动化(使用
webapp-selenium-testing)。
前提条件
| 要求 | 详情 |
|---|---|
| Node.js | 建议使用 v18+ |
| 包管理器 | npm、yarn 或 pnpm |
| Playwright | @playwright/test 包 |
| TypeScript | typescript + ts-node(可选,但建议使用) |
| 浏览器 | 通过 npx playwright install 安装 |
快速设置
# Initialize new project
npm init playwright@latest
# Or add to existing project
npm install -D @playwright/test
npx playwright install
首先要确认的问题
编写测试之前,请先确认:
- 应用 URL:本地开发服务的启动命令和端口,还是预发布环境 URL?
- 关键流程:必须覆盖哪些用户旅程(正常流程和错误状态)?
- 浏览器/设备:Chrome、Firefox、Safari?移动端视口?
- API 策略:真实后端、模拟响应,还是混合方式?
- 测试数据:是否有种子数据?如何重置/清理?
核心原则
1. 测试运行器和 TypeScript
始终使用 @playwright/test 和 TypeScript,以确保类型安全并获得更好的 IDE 支持。
import { test, expect } from "@playwright/test";
test("user can login", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page).toHaveURL(/.*dashboard/);
});
2. 定位器策略(优先级顺序)
优先使用基于角色且带有可访问名称的定位器(getByRole),然后依次使用 label → placeholder → text → test ID → CSS(最后手段)。绝不使用 XPath。
➡️ 完整的优先级层级、角色参考和示例: 定位器策略:优先级 — 唯一可信来源。
3. 自动等待和 Web 优先断言
Playwright 会自动等待元素。绝不要使用 sleep() 或任意超时。
// [ok] Web-first assertions (auto-retry)
await expect(page.getByRole("alert")).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId("status")).toHaveText("Success!");
// [no] Avoid manual waits
await page.waitForTimeout(2000); // Bad practice
4. 使用步骤组织测试
使用 test.step() 生成易读的报告,并便于定位失败原因:
test("checkout flow", async ({ page }) => {
await test.step("Add item to cart", async () => {
await page.goto("/products/1");
await page.getByRole("button", { name: "Add to Cart" }).click();
});
await test.step("Complete checkout", async () => {
await page.goto("/checkout");
await page.getByRole("button", { name: "Pay Now" }).click();
});
await test.step("Verify confirmation", async () => {
await expect(page.getByRole("heading")).toContainText("Order Confirmed");
});
});
关键工作流
表单和导航
// Form submit and wait for navigation (auto-waiting)
await page.getByRole("button", { name: "Login" }).click();
await expect(page).toHaveURL(/.*dashboard/);
// Form with API response validation
const responsePromise = page.waitForResponse(
(r) => r.url().includes("/api/login") && r.status() === 200,
);
await page.getByRole("button", { name: "Login" }).click();
const response = await responsePromise;
API 测试(Request Fixture)
test("API health check", async ({ request }) => {
const response = await request.get("/api/health");
expect(response.ok()).toBeTruthy();
expect(await response.json()).toMatchObject({ status: "ok" });
});
API 模拟和拦截
test("handles API error", async ({ page }) => {
await page.route("**/api/users", (route) =>
route.fulfill({
status: 500,
body: JSON.stringify({ error: "Server error" }),
}),
);
await page.goto("/users");
await expect(page.getByRole("alert")).toContainText("Something went wrong");
});
响应式测试
const viewports = [
{ width: 375, height: 667, name: "mobile" },
{ width: 768, height: 1024, name: "tablet" },
{ width: 1280, height: 720, name: "desktop" },
];
for (const vp of viewports) {
test(`navigation works on ${vp.name}`, async ({ page }) => {
await page.setViewportSize(vp);
await page.goto("/");
// Mobile: hamburger menu
if (vp.width < 768) {
await page.getByRole("button", { name: /menu/i }).click();
}
await page.getByRole("link", { name: "About" }).click();
await expect(page).toHaveURL(/about/);
});
}
配置
使用 playwright.config.ts 设置项目范围的配置:
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests",
retries: process.env.CI ? 2 : 0,
reporter: [["html"], ["junit", { outputFile: "results.xml" }]],
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
},
projects: [
{ name: "chromium", use: devices["Desktop Chrome"] },
{ name: "mobile", use: devices["Pixel 5"] },
],
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
故障排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 找不到元素 | 定位器错误或元素尚未渲染 | 使用 PWDEBUG=1 检查,并通过 getByRole 验证 |
| 等待超时 | 元素隐藏或加载缓慢 | 检查是否存在遮罩层,延长超时时间,使用 waitFor() |
| 测试不稳定 | 竞态条件、动画 | 添加 test.step(),使用适当的等待方式,禁用动画 |
| 严格模式违规 | 匹配到多个元素 | 使用 .first()、.filter() 或更具体的定位器 |
| 截图不一致 | 动态内容 | 遮盖动态区域,使用确定性数据 |
| CI 中失败,本地通过 | 环境差异 | 检查 baseURL、超时时间和 webServer 配置 |
| API 模拟无效 | 路由模式不匹配 | 使用 **/api/... glob 模式,并通过 page.on('request') 验证 |
CLI 快速参考
| 命令 | 说明 |
|---|---|
npx playwright test | 无头模式运行所有测试 |
npx playwright test --ui | 打开 UI 模式(交互式) |
npx playwright test --headed | 在可见浏览器中运行 |
npx playwright test --debug | 运行 Playwright Inspector |
npx playwright test -g "login" | 运行匹配模式的测试 |
npx playwright test --project=chromium | 运行指定项目 |
npx playwright show-report | 打开 HTML 报告 |
npx playwright codegen | 通过录制生成测试 |
PWDEBUG=1 npx playwright test | 使用 Inspector 调试 |
DEBUG=pw:api npx playwright test | 输出详细的 API 日志 |
危险信号
- 有 role、label 或 testId 可用时仍使用 CSS/XPath 定位器——这类定位器很脆弱,重构时容易失效。
- 使用
waitForTimeout/ 手动休眠,而不是采用 Web 优先、可自动重试的断言。 - 测试共享状态并依赖执行顺序——容易不稳定,且与执行顺序耦合。
- 断言只检查状态码或 URL,不验证可见状态——会掩盖渲染回归问题。
- 在多个测试中重复内联页面设置,而不使用 fixture——会造成重复和偏差。
参考资料
| 文档 | 内容 |
|---|---|
| 代码片段:设置 | 配置、身份验证设置、自定义夹具和日志记录 |
| 代码片段:交互 | 表单交互、API 测试和网络拦截 |
| 代码片段:视口和身份验证 | 响应式视口和身份验证模式 |
| 代码片段:断言和调试 | 断言、调试命令和实用辅助函数 |
| 定位器策略:优先级 | 定位器优先级层次结构和基于角色的定位器 |
| 定位器策略:文本 | 标签、文本、占位符、替代文本和测试 ID 定位器 |
| 定位器策略:筛选 | 筛选、链式调用和复杂定位器模式 |
| 定位器策略:反模式和调试 | 反模式、最后手段 CSS、调试和快速参考 |
| POM:基础知识 | POM 概念、目录结构、基础页面和流式 API |
| POM:组件 | 页面对象和可复用组件对象的实现 |
| POM:夹具 | 自定义和经过身份验证的页面对象夹具 |
| POM:实践 | 最佳实践、反模式和完整示例 |
| 调试:工具和 UI | 调试工具、UI 模式、Inspector 和有头模式 |
| 调试:跟踪和日志 | 跟踪查看器、详细日志、屏幕截图和视频 |
| 调试:错误和网络 | 控制台/页面错误和网络调试 |
| 调试:不稳定测试和定位器 | 不稳定测试修复、定位器调试和快速命令 |
验证
- 使用自定义夹具注入 — 规范文件中没有
new PageObject()调用;所有 POM 均通过夹具注入 - 定位器使用推荐策略 — 所有定位器均使用
getByRole()、getByTestId()或getByText();交互元素不使用 CSS 选择器 - 测试彼此独立 — 每个测试都会自行设置和清理状态;不使用共享可变状态的
beforeAll - 覆盖错误状态 — 至少有一个测试会在正常流程之外验证错误、空状态或加载状态
评论
支持 Markdown