6.3 KiB
6.3 KiB
User Journey 测试编写规范
📋 目录
测试架构
目录结构
e2e/
├── fixtures/ # 测试数据和 fixtures
│ └── test-data-factory.ts
├── journeys/ # User Journey 测试
│ ├── visitor/ # 访客旅程
│ ├── mobile/ # 移动端旅程
│ └── seo/ # SEO 验证旅程
├── pages/ # Page Objects
│ ├── frontend/ # 前端页面
│ └── admin/ # 后台管理页面
└── utils/ # 工具函数
└── test-reporter.ts
命名规范
测试文件
- 格式:
{场景}-journey.spec.ts - 示例:
conversion-journey.spec.ts,mobile-user-journey.spec.ts
测试用例
- 格式:
{用户角色}{动作}{预期结果} - 示例:
访客从首页浏览到提交咨询的完整旅程
Page Object 类
- 格式:
{Page}Page - 示例:
HomePage,ContactPage,AdminNewsPage
Page Object 模式
原则
- 单一职责: 每个 Page Object 只负责一个页面
- 封装实现: 隐藏页面实现细节,暴露业务方法
- 可复用: 方法设计应考虑多个测试场景复用
示例
import { Page, expect } from '@playwright/test';
export class FrontendContactPage {
readonly page: Page;
constructor(page: Page) {
this.page = page;
}
async goto() {
await this.page.goto('/contact');
await this.page.waitForLoadState('domcontentloaded');
}
async fillForm(data: ContactFormData) {
await this.page.fill('input[name="name"]', data.name);
await this.page.fill('input[name="email"]', data.email);
await this.page.fill('textarea[name="message"]', data.message);
}
async submitForm() {
await this.page.click('button[type="submit"]');
}
async expectSubmitSuccess() {
await expect(
this.page.locator('text=提交成功')
).toBeVisible({ timeout: 10000 });
}
}
测试数据管理
使用 TestDataFactory
import { TestDataFactory } from '../fixtures/test-data-factory';
// 创建默认测试数据
const contactData = TestDataFactory.createContactForm();
// 创建自定义测试数据
const customData = TestDataFactory.createContactForm({
name: '自定义用户',
email: 'custom@example.com',
});
数据隔离原则
- 唯一性: 使用时间戳确保数据唯一
- 可追溯: 数据命名包含测试场景标识
- 清理机制: 测试后清理创建的数据
测试结构
标准 Journey 测试结构
import { test, expect } from '@playwright/test';
import { FrontendHomePage, FrontendContactPage } from '../pages/frontend';
import { TestDataFactory } from '../fixtures/test-data-factory';
test.describe('用户旅程描述 @journey @tag', () => {
let homePage: FrontendHomePage;
let contactPage: FrontendContactPage;
test.beforeEach(async ({ page }) => {
homePage = new FrontendHomePage(page);
contactPage = new FrontendContactPage(page);
});
test('完整旅程描述', async () => {
const testData = TestDataFactory.createContactForm();
await test.step('步骤1: 初始状态', async () => {
await homePage.goto();
await homePage.expectHeroVisible();
});
await test.step('步骤2: 用户行为', async () => {
await homePage.clickCTAButton();
});
await test.step('步骤3: 验证结果', async () => {
await contactPage.expectSubmitSuccess();
});
});
});
最佳实践
✅ 应该做的
-
使用 test.step 组织测试步骤
await test.step('清晰的步骤描述', async () => { // 测试逻辑 }); -
使用 Page Object 封装页面操作
await homePage.goto(); await homePage.expectHeroVisible(); -
使用 TestDataFactory 生成测试数据
const data = TestDataFactory.createContactForm(); -
添加清晰的断言
await expect(page.locator('h1')).toBeVisible(); await expect(page).toHaveTitle(/关键词/); -
使用标签分类测试
test.describe('访客旅程 @journey @visitor @conversion', () => { // ... });
❌ 不应该做的
-
不要直接操作 page 对象
// ❌ 错误 await page.fill('input[name="name"]', 'test'); // ✅ 正确 await contactPage.fillForm(data); -
不要硬编码测试数据
// ❌ 错误 await page.fill('input[name="name"]', '测试用户'); // ✅ 正确 const data = TestDataFactory.createContactForm(); await contactPage.fillForm(data); -
不要使用过长的等待
// ❌ 错误 await page.waitForTimeout(5000); // ✅ 正确 await page.waitForLoadState('domcontentloaded'); await expect(element).toBeVisible({ timeout: 10000 });
测试标签体系
| 标签 | 用途 | 示例 |
|---|---|---|
@journey |
所有 User Journey 测试 | @journey |
@visitor |
访客相关测试 | @visitor |
@user |
已登录用户测试 | @user |
@admin |
管理员测试 | @admin |
@mobile |
移动端测试 | @mobile |
@seo |
SEO 相关测试 | @seo |
@conversion |
转化流程测试 | @conversion |
运行特定标签的测试
# 运行所有 journey 测试
npx playwright test --grep "@journey"
# 运行移动端测试
npx playwright test --grep "@mobile"
# 运行 SEO 测试
npx playwright test --grep "@seo"
质量标准
测试覆盖率目标
- User Journey 覆盖率: 100%
- Page Object 覆盖率: 100%
- 关键业务流程: 必须覆盖
测试质量指标
- 通过率: ≥ 95%
- 平均执行时间: < 5秒/测试
- Flaky 测试率: < 2%