# 分层测试最佳实践 ## 概述 本文档提供分层测试系统的最佳实践,帮助团队构建高效、可靠的测试体系。 ## 核心原则 ### 1. 质量左移 在需求分析和设计阶段就考虑测试策略,而不是在开发完成后才补充测试。 **实践:** - 在需求文档中明确测试要求 - 在设计评审中讨论可测试性 - 开发过程中同步编写测试 ### 2. 测试金字塔 遵循测试金字塔原则,保持合理的测试比例: ``` /\ / \ E2E测试 (10%) /____\ / \ 集成测试 (30%) /________\ / \ 单元测试 (60%) /____________\ ``` **实践:** - 单元测试:快速、独立、覆盖核心逻辑 - 集成测试:验证组件间交互 - E2E测试:验证关键用户流程 ### 3. 快速反馈 确保测试能够快速提供反馈,帮助开发人员快速定位问题。 **实践:** - 快速层测试在5分钟内完成 - 标准层测试在30分钟内完成 - 深度层测试可以接受较长执行时间 ## 测试分层策略 ### 快速层设计 **目标:** 在5分钟内验证核心功能 **包含内容:** 1. **冒烟测试** (Smoke Tests) - 验证应用能够正常启动 - 验证关键页面能够加载 - 验证核心API能够响应 2. **API测试** - 验证API端点的正确性 - 验证数据格式和结构 - 验证错误处理 3. **基础功能测试** - 验证用户登录/登出 - 验证基本CRUD操作 - 验证权限控制 **最佳实践:** - 每个测试文件不超过3个测试用例 - 每个测试用例执行时间不超过10秒 - 使用mock数据替代真实数据库 **示例:** ```typescript test.describe('用户认证快速测试 @smoke @critical', () => { test('应该能够成功登录', async ({ page }) => { await page.goto('/login'); await page.fill('[data-testid="email"]', 'admin@example.com'); await page.fill('[data-testid="password"]', 'password123'); await page.click('[data-testid="login-btn"]'); await expect(page).toHaveURL('/dashboard'); }); test('应该能够成功登出', async ({ page }) => { await page.goto('/dashboard'); await page.click('[data-testid="logout-btn"]'); await expect(page).toHaveURL('/login'); }); }); ``` ### 标准层设计 **目标:** 在30分钟内验证大部分功能 **包含内容:** 1. **功能测试** (Functional Tests) - 验证完整的用户流程 - 验证表单验证 - 验证业务规则 2. **响应式测试** (Responsive Tests) - 验证不同屏幕尺寸下的布局 - 验证移动端和桌面端的交互 - 验证触摸和鼠标事件 3. **管理后台测试** (Admin Tests) - 验证内容管理功能 - 验证用户管理功能 - 验证系统配置 **最佳实践:** - 每个测试文件包含5-10个测试用例 - 每个测试用例执行时间不超过30秒 - 使用Page Object Model模式 **示例:** ```typescript test.describe('新闻管理功能测试 @admin @regression', () => { test.beforeEach(async ({ page }) => { await page.goto('/admin/news'); }); test('应该能够创建新闻', async ({ page }) => { await page.click('[data-testid="create-news-btn"]'); await page.fill('[data-testid="news-title"]', '测试新闻'); await page.fill('[data-testid="news-content"]', '新闻内容'); await page.click('[data-testid="save-btn"]'); await expect(page.locator('[data-testid="success-message"]')).toBeVisible(); }); test('应该能够编辑新闻', async ({ page }) => { await page.click('[data-testid="edit-news-1"]'); await page.fill('[data-testid="news-title"]', '更新后的标题'); await page.click('[data-testid="save-btn"]'); await expect(page.locator('[data-testid="news-title"]')).toHaveValue('更新后的标题'); }); test('应该能够删除新闻', async ({ page }) => { await page.click('[data-testid="delete-news-1"]'); await page.click('[data-testid="confirm-btn"]'); await expect(page.locator('[data-testid="news-1"]')).not.toBeVisible(); }); }); ``` ### 深度层设计 **目标:** 在发布前进行全面验证 **包含内容:** 1. **视觉回归测试** (Visual Regression Tests) - 验证UI与设计稿一致 - 验证样式和布局 - 验证跨浏览器一致性 2. **性能测试** (Performance Tests) - 验证页面加载时间 - 验证API响应时间 - 验证资源加载优化 3. **完整回归测试** (Full Regression Tests) - 验证所有已知功能 - 验证边界情况 - 验证错误处理 **最佳实践:** - 使用截图对比工具 - 使用性能监控工具 - 在夜间或周末执行 **示例:** ```typescript test.describe('首页视觉回归测试 @visual @regression', () => { test('桌面端首页应该与基准一致', async ({ page }) => { await page.setViewportSize({ width: 1280, height: 720 }); await page.goto('/'); await expect(page).toHaveScreenshot('homepage-desktop.png'); }); test('移动端首页应该与基准一致', async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/'); await expect(page).toHaveScreenshot('homepage-mobile.png'); }); }); ``` ## 测试标记策略 ### 标记分类 #### 优先级标记 - `@critical` - 关键测试,必须通过 - `@high` - 高优先级测试 - `@medium` - 中等优先级测试 - `@low` - 低优先级测试 #### 类型标记 - `@smoke` - 冒烟测试 - `@regression` - 回归测试 - `@functional` - 功能测试 - `@api` - API测试 - `@visual` - 视觉测试 - `@performance` - 性能测试 #### 平台标记 - `@desktop` - 桌面端测试 - `@mobile` - 移动端测试 - `@tablet` - 平板端测试 #### 功能标记 - `@auth` - 认证相关测试 - `@admin` - 管理后台测试 - `@content` - 内容管理测试 - `@user` - 用户功能测试 ### 标记使用规则 1. **每个测试套件至少有一个标记** 2. **关键测试必须标记为 `@critical`** 3. **冒烟测试必须标记为 `@smoke`** 4. **回归测试必须标记为 `@regression`** ## 性能优化 ### 减少测试执行时间 #### 1. 并行执行 ```typescript // playwright.config.tiered.ts { fullyParallel: true, workers: '75%', } ``` #### 2. 减少等待时间 ```typescript // 不推荐 await page.waitForTimeout(5000); // 推荐 await page.waitForSelector('[data-testid="result"]', { timeout: 5000 }); ``` #### 3. 使用快速选择器 ```typescript // 不推荐 await page.click('div > div > button'); // 推荐 await page.click('[data-testid="submit-btn"]'); ``` #### 4. 复用浏览器上下文 ```typescript test.describe('用户管理测试', () => { test.use({ storageState: '.auth/admin.json' }); test('应该能够创建用户', async ({ page }) => { // 测试逻辑 }); }); ``` ### 优化测试数据 #### 1. 使用固定数据 ```typescript const testUser = { email: 'test@example.com', password: 'password123', }; test('应该能够登录', async ({ page }) => { await page.fill('[data-testid="email"]', testUser.email); await page.fill('[data-testid="password"]', testUser.password); }); ``` #### 2. 使用测试数据库 ```typescript test.beforeEach(async () => { await db.reset(); await db.seed(testData); }); ``` #### 3. 清理测试数据 ```typescript test.afterEach(async () => { await db.cleanup(); }); ``` ## 可维护性 ### Page Object Model 使用Page Object Model模式提高测试的可维护性: ```typescript // pages/LoginPage.ts export class LoginPage { constructor(private page: Page) {} async login(email: string, password: string) { await this.page.fill('[data-testid="email"]', email); await this.page.fill('[data-testid="password"]', password); await this.page.click('[data-testid="login-btn"]'); } async expectLoggedIn() { await expect(this.page).toHaveURL('/dashboard'); } } // tests/login.spec.ts test('应该能够登录', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.login('admin@example.com', 'password123'); await loginPage.expectLoggedIn(); }); ``` ### 测试数据管理 使用专门的测试数据管理器: ```typescript // utils/test-data.ts export const TestData = { users: { admin: { email: 'admin@example.com', password: 'password123', role: 'admin', }, user: { email: 'user@example.com', password: 'password123', role: 'user', }, }, news: { valid: { title: '测试新闻', content: '新闻内容', }, invalid: { title: '', content: '', }, }, }; ``` ### 配置管理 使用环境变量管理测试配置: ```typescript // config/environments.ts export const getEnvironment = () => { const env = process.env.NODE_ENV || 'development'; return { baseURL: process.env.BASE_URL || 'http://localhost:3000', timeout: parseInt(process.env.TEST_TIMEOUT || '30000'), retries: parseInt(process.env.TEST_RETRIES || '2'), headless: process.env.HEADLESS !== 'false', }; }; ``` ## 持续改进 ### 定期审查 每月进行一次测试审查: 1. 检查测试覆盖率 2. 识别慢速测试 3. 评估测试有效性 4. 清理无用测试 ### 性能监控 持续监控测试性能: 1. 记录测试执行时间 2. 识别性能趋势 3. 优化慢速测试 4. 调整测试分层 ### 反馈收集 收集测试反馈: 1. 开发人员反馈 2. 测试失败分析 3. 用户反馈 4. 生产问题追踪 ## 常见问题 ### Q: 如何确定测试应该放在哪一层? A: 根据测试的执行时间和重要性: - 执行时间<30秒且是关键功能 → 快速层 - 执行时间<60秒 → 标准层 - 执行时间>60秒或需要完整回归 → 深度层 ### Q: 测试失败时如何处理? A: 按照以下优先级处理: 1. 快速层测试失败 → 立即修复 2. 标准层测试失败 → 在合并PR前修复 3. 深度层测试失败 → 在发布前修复 ### Q: 如何减少测试执行时间? A: 采用以下策略: 1. 并行执行测试 2. 减少不必要的等待 3. 优化选择器 4. 拆分大测试 5. 使用mock数据 ### Q: 如何提高测试稳定性? A: 遵循以下原则: 1. 使用稳定的等待策略 2. 避免硬编码的等待时间 3. 使用data-testid选择器 4. 清理测试数据 5. 增加重试次数 ## 参考资源 - [Playwright最佳实践](https://playwright.dev/docs/best-practices) - [测试金字塔](https://martinfowler.com/articles/practical-test-pyramid.html) - [Page Object Model](https://playwright.dev/docs/pom) - [测试驱动开发](https://martinfowler.com/bliki/TestDrivenDevelopment.html)