Agent skill
e2e-optimizer
Playwright E2E 敏捷测试优化:时间优先,精确打击。禁止全局运行,只测改动点,强制 Mock 外部依赖。触发词:E2E 测试、Playwright 测试、跑测试、测试优化。
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/e2e-optimizer
SKILL.md
E2E Optimizer - Playwright 敏捷测试规范
核心理念:获取最快的反馈,而不是追求最严谨的测试覆盖率
公式:精确打击 × Mock 依赖 = 秒级反馈
🎯 核心认知
在自主开发循环中,时间是最宝贵的资源。全局 E2E 测试极其耗时且脆弱。
| 传统测试思维 | 敏捷测试思维 |
|---|---|
| "运行全部测试确保没问题" | "只测改动点,秒级反馈" |
| 等待 30 秒跑完 100 个用例 | 3 秒内跑 1 个核心用例 |
| 追求 100% 覆盖率 | 追求 MVU 核心路径验证 |
| 测试失败就调试到底 | 连续失败 3 次就 skip 或降级 |
根本解法:用测试金字塔和精确打击强制收缩范围
🛑 绝对红线(禁止行为)
| 行为 | 为什么禁止 | 正确做法 |
|---|---|---|
❌ npx playwright test(无参数) |
全局运行耗时太长 | npx playwright test tests/login.spec.ts |
❌ page.waitForTimeout(5000) |
硬编码死等,浪费时间 | await page.waitForSelector('.success') |
| ❌ 连续失败 3 次还执着调试 | 阻塞主流程推进 | 标记 .skip 或降级为单元测试 |
| ❌ 所有逻辑都用 E2E 测 | 测试金字塔倒置 | 纯逻辑用 Vitest/Jest 单元测试 |
✅ 智能体执行准则
1. 精确打击(只测改动点)
修改了某个组件或页面时,只运行对应的单个测试文件/用例:
# ✅ 只跑单个文件
npx playwright test tests/login.spec.ts
# ✅ 只跑特定用例 + 失败即停
npx playwright test tests/login.spec.ts -g "should login successfully" --max-failures=1
# ✅ 只跑改动的测试(配合 git diff)
npx playwright test --grep "@smoke"
# ✅ 限制并发数,避免资源竞争
npx playwright test --workers=2
2. 拥抱测试金字塔(Shift-Left Testing)
┌─────────────┐
│ E2E 测试 │ ← 仅验证核心用户路径 (1-2 条)
│ (Playwright)│ 5-10 秒反馈
├───────────────┤
│ 组件/集成测试 │ ← 组件交互、模块集成
│ (Testing Lib)│ 1-3 秒反馈
├─────────────────┤
│ 单元测试 │ ← 纯逻辑、数据转换、工具函数
│ (Vitest/Jest) │ 毫秒级反馈
└─────────────────┘
决策树:
要测试的代码是什么类型?
│
├── 纯逻辑/数据转换/工具函数
│ └──> Vitest/Jest 单元测试(毫秒级)
│
├── 组件渲染/UI 交互
│ └──> Testing Library 组件测试(秒级)
│
└── 完整用户路径/跨模块集成
└──> Playwright E2E(只测 Happy Path)
3. 强制 Mock 外部依赖
E2E 耗时往往是因为等待后端 API 或第三方服务。强制拦截并 Mock 网络请求:
// ✅ Mock API 响应,避免真实网络等待
await page.route('**/api/v1/data', async route => {
const json = { mock: "data", status: "success" };
await route.fulfill({ json });
});
// ✅ Mock 延迟响应,模拟慢速网络
await page.route('**/api/slow', async route => {
await new Promise(resolve => setTimeout(resolve, 1000));
await route.fulfill({ json: { data: "mocked" } });
});
// ✅ 阻断外部 CDN/统计脚本,避免拖慢测试
await page.route('**/analytics.js', route => route.abort());
Mock 优先级:
| 请求类型 | 策略 |
|---|---|
| 后端 API | 全部 Mock,返回固定数据 |
| 第三方服务(支付/短信) | 全部 Mock |
| CDN/静态资源 | 允许或阻断(不等待) |
| 图片/字体 | 阻断(route.abort()) |
快速命令参考
运行测试
# 运行单个文件
npx playwright test tests/login.spec.ts
# 运行单个用例
npx playwright test -g "should login"
# 运行并生成报告
npx playwright test --reporter=html
# 调试模式(带 UI)
npx playwright test --debug
# 显示浏览器(有头模式)
npx playwright test --headed
# 限制失败次数,快速失败
npx playwright test --max-failures=1
代码生成
# 录制测试脚本
npx playwright codegen https://example.com
# 生成 TypeScript 测试
npx playwright codegen --language=typescript https://example.com
环境配置
# 使用特定环境配置
BASE_URL=http://localhost:3000 npx playwright test
# 使用 production 环境
BASE_URL=https://prod.example.com npx playwright test
测试文件结构规范
tests/
├── e2e/ # E2E 测试(只测核心路径)
│ ├── login.spec.ts # 登录流程
│ └── checkout.spec.ts # 下单流程
├── integration/ # 集成测试(模块间交互)
│ └── api-integration.spec.ts
└── unit/ # 单元测试(纯逻辑)
└── utils.spec.ts
单个测试文件模板:
import { test, expect } from '@playwright/test';
test.describe('登录模块', () => {
// ✅ 只测试 Happy Path
test('应该登录成功', async ({ page }) => {
// Mock API
await page.route('**/api/login', async route => {
await route.fulfill({ json: { token: 'mock-token' } });
});
await page.goto('/login');
await page.fill('#username', 'test');
await page.fill('#password', 'test');
await page.click('button[type="submit"]');
// 用明确的条件等待,不要用 setTimeout
await expect(page.locator('.welcome')).toBeVisible();
});
// ❌ 跳过不稳定测试
test.skip('边界情况测试', async ({ page }) => {
// 留到有时间再处理
});
});
与时间盒 Skill 协作
工作流
时间盒启动(10-30 分钟)
↓
1. 定义 MVU 核心路径
2. 为 MVU 编写 1-2 个 E2E 测试(只测 Happy Path)
3. 运行测试验证
↓
时间盒执行
↓
- 测试失败 >3 次 → skip 或降级
- 测试通过 → 继续开发
- 新改动 → 只跑相关测试
↓
时间盒结束
↓
- 提交测试代码
- 记录 TODO(边界情况后续补充)
时间分配建议
| 任务类型 | E2E 测试时间 | 单元测试时间 |
|---|---|---|
| 小功能/修复 | 2min(1 个用例) | 5min |
| 中等功能 | 5min(2-3 个用例) | 10min |
| 大功能 | 10min(核心路径) | 20min |
原则:
- E2E 测试数量 ≤ 3 个核心用例
- 单元测试覆盖所有逻辑分支
- 单个 E2E 测试运行时间 < 10 秒
调试故障排查
测试超时
# 检查是否使用了 waitForTimeout
grep -r "waitForTimeout" tests/
# 替换为条件等待
# ❌ await page.waitForTimeout(5000);
# ✅ await page.waitForSelector('.success');
测试不稳定(Flaky)
// ✅ 使用重试机制(仅限已知不稳定测试)
test('不稳定测试', async ({ page }) => {
// ...
}, { retries: 2 });
// ✅ 或标记为 skip,记录问题
test.skip('不稳定测试 - 待修复', () => {});
网络请求超时
// 增加超时时间
test.setTimeout(30000);
// 或全局配置 playwright.config.ts
export default {
timeout: 30000,
expect: {
timeout: 10000
}
};
与其他 Skill 协作
| Skill | 协作方式 |
|---|---|
| time-boxing | 每个时间盒内只写 1-3 个核心 E2E 测试 |
| git-commit | 测试代码随功能一起提交 |
| github | PR 自动运行相关测试(CI 配置) |
触发词
| 优先级 | 触发词 | 动作 |
|---|---|---|
| 高 | E2E 测试、Playwright 测试、跑测试 | 运行指定测试 |
| 高 | 测试失败、又失败了 | 失败>3 次 → skip 或降级 |
| 高 | Mock API、拦截请求 | 添加 route.fulfill() |
| 中 | 测试太慢、优化测试 | 检查 waitForTimeout,替换为条件等待 |
| 中 | 只测这个、单跑 | 运行单个测试文件/用例 |
Playwright 配置模板
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
// 超时设置
timeout: 30000,
expect: {
timeout: 10000
},
// 并发设置
fullyParallel: true,
workers: 2, // 限制并发数
// 失败处理
maxFailures: 3, // 超过 3 个失败就停止
// 重试设置
retries: 1, // 失败自动重试 1 次
// 报告生成
reporter: [
['list'],
['html', { open: 'never' }]
],
// 全局 Mock(可选)
use: {
baseURL: process.env.BASE_URL || 'http://localhost:3000',
screenshot: 'only-on-failure',
video: 'retain-on-failure'
}
});
反模式(禁止行为)
| 行为 | 为什么错 | 正确做法 |
|---|---|---|
❌ npx playwright test 无参数 |
全局运行耗时太长 | 指定文件或用例 |
❌ waitForTimeout(5000) |
死等,浪费时间 | waitForSelector 条件等待 |
| ❌ 所有逻辑都用 E2E 测 | 测试倒置,反馈慢 | 单元测试 + 组件测试 + E2E |
| ❌ 测试失败执着调试 | 阻塞主流程 | 失败>3 次就 skip 或降级 |
| ❌ 测试不 Mock 外部依赖 | 网络不稳定导致失败 | 强制 Mock API |
| ❌ 追求 100% 覆盖率 | 测试成本过高 | 只测核心路径(Happy Path) |
示例:登录功能测试
❌ 错误示例(慢 + 不稳定)
test('登录', async ({ page }) => {
await page.goto('/login');
await page.fill('#username', 'test');
await page.fill('#password', 'test');
await page.click('button');
// ❌ 硬编码等待
await page.waitForTimeout(5000);
// ❌ 没有 Mock,依赖真实后端
await expect(page.locator('.dashboard')).toBeVisible();
});
✅ 正确示例(快 + 稳定)
test('登录', async ({ page }) => {
// ✅ Mock API
await page.route('**/api/login', async route => {
await route.fulfill({
json: { token: 'mock-token', user: 'test' },
status: 200
});
});
await page.goto('/login');
await page.fill('#username', 'test');
await page.fill('#password', 'test');
await page.click('button');
// ✅ 条件等待
await expect(page.locator('.welcome')).toBeVisible({ timeout: 5000 });
});
// 运行命令:
// npx playwright test tests/login.spec.ts
// 预期耗时:<3 秒
快速检查清单
运行 E2E 测试前,确认:
# 1. 是否只跑改动点?
npx playwright test tests/<changed-file>.spec.ts
# 2. 是否限制了失败次数?
npx playwright test --max-failures=1
# 3. 是否使用了 Mock?
grep -r "page.route" tests/
# 4. 是否有硬编码等待?
grep -r "waitForTimeout" tests/
核心原则
反馈速度优先,覆盖率其次
- 精确打击 - 只测改动点,不跑全局
- Mock 优先 - 拦截外部依赖,避免网络等待
- 条件等待 - 用
waitForSelector代替waitForTimeout - 测试金字塔 - E2E 只测核心路径,逻辑用单元测试
- 快速失败 - 失败>3 次就 skip 或降级
- 秒级反馈 - 单个测试运行时间 <10 秒
版本:v1.0 | 最后更新:2026-03-18
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?