docs: 整理文档结构并创建索引(任务 2.3/20)
This commit is contained in:
@@ -0,0 +1,450 @@
|
||||
# 分层测试最佳实践
|
||||
|
||||
## 概述
|
||||
|
||||
本文档提供分层测试系统的最佳实践,帮助团队构建高效、可靠的测试体系。
|
||||
|
||||
## 核心原则
|
||||
|
||||
### 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)
|
||||
Reference in New Issue
Block a user