docs: 整理文档结构并创建索引(任务 2.3/20)
This commit is contained in:
@@ -0,0 +1,445 @@
|
||||
# Allure 测试报告使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
Allure Framework 是一个灵活的、轻量级的、多语言测试报告工具,它不仅以简洁的Web报告形式展示测试结果,还提供了完整的测试执行历史记录。
|
||||
|
||||
## 安装状态
|
||||
|
||||
✅ **已安装组件**:
|
||||
- `allure-playwright`: ^3.5.0 (Playwright集成)
|
||||
- `allure-commandline`: ^2.37.0 (命令行工具)
|
||||
|
||||
✅ **已配置**:
|
||||
- Playwright配置文件中已集成Allure reporter
|
||||
- 分层测试配置支持Allure报告生成
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 运行测试并生成报告
|
||||
|
||||
```bash
|
||||
# 运行分层测试(会自动生成allure-results)
|
||||
npm run test:tier:fast
|
||||
npm run test:tier:standard
|
||||
npm run test:tier:deep
|
||||
|
||||
# 或者运行所有分层测试
|
||||
npm run test:tier:all
|
||||
```
|
||||
|
||||
### 2. 生成HTML报告
|
||||
|
||||
```bash
|
||||
# 生成Allure报告
|
||||
npm run test:allure
|
||||
|
||||
# 或者直接打开报告
|
||||
npm run test:allure:open
|
||||
|
||||
# 或者实时serve报告
|
||||
npm run test:allure:serve
|
||||
```
|
||||
|
||||
## 报告功能
|
||||
|
||||
### 📊 测试概览
|
||||
|
||||
Allure报告提供以下信息:
|
||||
|
||||
1. **测试统计**
|
||||
- 总测试数
|
||||
- 通过/失败/跳过数量
|
||||
- 成功率
|
||||
- 执行时间
|
||||
|
||||
2. **测试分类**
|
||||
- 按套件分组
|
||||
- 按标签分组(@smoke, @regression等)
|
||||
- 按严重程度分组
|
||||
|
||||
3. **历史趋势**
|
||||
- 测试结果历史对比
|
||||
- 失败率趋势
|
||||
- 执行时间趋势
|
||||
|
||||
### 📈 详细信息
|
||||
|
||||
每个测试用例包含:
|
||||
|
||||
- **测试步骤**: 详细的执行步骤
|
||||
- **附件**: 截图、视频、日志
|
||||
- **参数**: 测试参数和配置
|
||||
- **时间线**: 执行时间分布
|
||||
- **环境信息**: 浏览器、操作系统等
|
||||
|
||||
### 🎯 测试分层支持
|
||||
|
||||
分层测试配置已集成Allure报告:
|
||||
|
||||
| 测试层级 | 配置文件 | 报告输出 |
|
||||
|---------|---------|---------|
|
||||
| Fast | playwright.config.tiered.ts | allure-results/ |
|
||||
| Standard | playwright.config.tiered.ts | allure-results/ |
|
||||
| Deep | playwright.config.tiered.ts | allure-results/ |
|
||||
|
||||
## 使用场景
|
||||
|
||||
### 场景1: 本地开发调试
|
||||
|
||||
```bash
|
||||
# 1. 运行特定测试
|
||||
cd e2e
|
||||
npx playwright test --grep "contact-form"
|
||||
|
||||
# 2. 实时查看报告
|
||||
npm run test:allure:serve
|
||||
```
|
||||
|
||||
### 场景2: CI/CD集成
|
||||
|
||||
```yaml
|
||||
# .woodpecker.yml示例
|
||||
pipeline:
|
||||
test:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm run test:tier:fast
|
||||
- npm run test:tier:standard
|
||||
- npm run test:allure
|
||||
when:
|
||||
event: [push, pull_request]
|
||||
|
||||
publish-report:
|
||||
image: node:18
|
||||
commands:
|
||||
- allure generate allure-results -o allure-report
|
||||
when:
|
||||
status: [success, failure]
|
||||
```
|
||||
|
||||
### 场景3: 测试失败分析
|
||||
|
||||
```bash
|
||||
# 1. 运行失败的测试
|
||||
cd e2e
|
||||
npx playwright test --last-failed
|
||||
|
||||
# 2. 查看详细报告
|
||||
npm run test:allure:open
|
||||
|
||||
# 3. 在报告中查看:
|
||||
# - 失败的断言
|
||||
# - 错误堆栈
|
||||
# - 截图和视频
|
||||
# - 执行日志
|
||||
```
|
||||
|
||||
## 报告定制
|
||||
|
||||
### 添加测试标签
|
||||
|
||||
在测试文件中添加标签:
|
||||
|
||||
```typescript
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('用户登录测试 @smoke @critical', async ({ page }) => {
|
||||
// 测试代码
|
||||
});
|
||||
```
|
||||
|
||||
### 添加测试步骤
|
||||
|
||||
```typescript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('复杂测试流程', async ({ page }) => {
|
||||
await test.step('打开登录页面', async () => {
|
||||
await page.goto('/login');
|
||||
});
|
||||
|
||||
await test.step('输入用户名', async () => {
|
||||
await page.fill('#username', 'test@example.com');
|
||||
});
|
||||
|
||||
await test.step('输入密码', async () => {
|
||||
await page.fill('#password', 'password123');
|
||||
});
|
||||
|
||||
await test.step('提交表单', async () => {
|
||||
await page.click('#submit');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 添加附件
|
||||
|
||||
```typescript
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('带附件的测试', async ({ page }, testInfo) => {
|
||||
await page.goto('/');
|
||||
|
||||
// 添加截图
|
||||
const screenshot = await page.screenshot();
|
||||
await testInfo.attach('首页截图', {
|
||||
body: screenshot,
|
||||
contentType: 'image/png'
|
||||
});
|
||||
|
||||
// 添加文本日志
|
||||
await testInfo.attach('测试日志', {
|
||||
body: '这是测试日志内容',
|
||||
contentType: 'text/plain'
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 报告分析技巧
|
||||
|
||||
### 1. 识别不稳定测试
|
||||
|
||||
在报告的"Categories"部分查看:
|
||||
- 标记为"Product defects"的测试:真正的bug
|
||||
- 标记为"Test defects"的测试:测试代码问题
|
||||
|
||||
### 2. 性能分析
|
||||
|
||||
在"Timeline"标签页:
|
||||
- 查看测试执行时间分布
|
||||
- 识别慢速测试
|
||||
- 优化测试性能
|
||||
|
||||
### 3. 失败模式分析
|
||||
|
||||
在"Graphs"标签页:
|
||||
- 查看失败率趋势
|
||||
- 识别常见失败原因
|
||||
- 追踪测试稳定性
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **使用有意义的测试名称**
|
||||
```typescript
|
||||
test('用户应该能够成功登录 @smoke', async ({ page }) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
2. **添加详细的测试步骤**
|
||||
- 每个关键操作都是一个step
|
||||
- 步骤名称清晰描述操作
|
||||
|
||||
3. **为失败测试添加附件**
|
||||
- 失败时自动截图
|
||||
- 保存页面HTML
|
||||
- 记录控制台日志
|
||||
|
||||
4. **使用标签分类测试**
|
||||
- @smoke: 冒烟测试
|
||||
- @regression: 回归测试
|
||||
- @critical: 关键测试
|
||||
- @flaky: 不稳定测试
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要在报告中包含敏感信息**
|
||||
- 密码
|
||||
- API密钥
|
||||
- 个人数据
|
||||
|
||||
2. **不要过度使用附件**
|
||||
- 只在必要时添加
|
||||
- 避免报告过大
|
||||
|
||||
3. **不要忽略失败测试**
|
||||
- 及时修复或标记
|
||||
- 分析根本原因
|
||||
|
||||
## CI/CD集成示例
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
```yaml
|
||||
name: Test with Allure Report
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm ci
|
||||
cd e2e && npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run test:tier:fast
|
||||
npm run test:tier:standard
|
||||
|
||||
- name: Generate Allure Report
|
||||
if: always()
|
||||
run: npm run test:allure
|
||||
|
||||
- name: Upload Allure Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: allure-report
|
||||
path: e2e/allure-report
|
||||
```
|
||||
|
||||
### Woodpecker CI
|
||||
|
||||
```yaml
|
||||
pipeline:
|
||||
install:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm ci
|
||||
- cd e2e && npm ci
|
||||
|
||||
test:
|
||||
image: node:18
|
||||
commands:
|
||||
- npm run test:tier:fast
|
||||
- npm run test:tier:standard
|
||||
when:
|
||||
event: [push, pull_request]
|
||||
|
||||
report:
|
||||
image: node:18
|
||||
commands:
|
||||
- cd e2e && npm run test:allure
|
||||
when:
|
||||
status: [success, failure]
|
||||
|
||||
publish:
|
||||
image: node:18
|
||||
commands:
|
||||
- cd e2e && npm run test:allure:open
|
||||
when:
|
||||
status: [success, failure]
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1: 报告无法生成
|
||||
|
||||
**症状**: 运行`npm run test:allure`时报错
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查allure-commandline是否安装
|
||||
cd e2e
|
||||
npm ls allure-commandline
|
||||
|
||||
# 如果未安装,重新安装
|
||||
npm install --save-dev allure-commandline
|
||||
```
|
||||
|
||||
### 问题2: 报告内容为空
|
||||
|
||||
**症状**: 报告生成成功,但没有测试数据
|
||||
|
||||
**解决方案**:
|
||||
```bash
|
||||
# 检查allure-results目录
|
||||
ls -la e2e/allure-results
|
||||
|
||||
# 确保测试已运行
|
||||
npm run test:tier:fast
|
||||
```
|
||||
|
||||
### 问题3: 截图未显示
|
||||
|
||||
**症状**: 报告中看不到截图
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 确保Playwright配置中启用了截图
|
||||
use: {
|
||||
screenshot: 'only-on-failure', // 或 'on'
|
||||
video: 'retain-on-failure',
|
||||
trace: 'retain-on-failure',
|
||||
}
|
||||
```
|
||||
|
||||
## 进阶功能
|
||||
|
||||
### 1. 自定义报告配置
|
||||
|
||||
创建`allure.config.js`:
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
resultsDir: 'allure-results',
|
||||
reportDir: 'allure-report',
|
||||
cleanResultsDir: true,
|
||||
cleanReportDir: true,
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 集成到测试框架
|
||||
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
import { defineConfig } from '@playwright/test';
|
||||
|
||||
export default defineConfig({
|
||||
reporter: [
|
||||
['allure-playwright', {
|
||||
outputFolder: 'allure-results',
|
||||
detail: true,
|
||||
suiteTitle: false,
|
||||
}],
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### 3. 报告历史记录
|
||||
|
||||
使用Allure TestOps或Allure Report Server保存历史报告:
|
||||
|
||||
```bash
|
||||
# 安装Allure Report Server
|
||||
npm install -g allure-report-server
|
||||
|
||||
# 启动服务器
|
||||
allure-report-server --port 8080
|
||||
```
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Allure官方文档](https://docs.qameta.io/allure/)
|
||||
- [Allure Playwright集成](https://github.com/allure-framework/allure-js)
|
||||
- [Playwright测试最佳实践](https://playwright.dev/docs/best-practices)
|
||||
- [测试分层指南](./test-tiering-best-practices.md)
|
||||
|
||||
## 总结
|
||||
|
||||
Allure测试报告已完全集成到项目中,提供了:
|
||||
|
||||
✅ **自动化报告生成**
|
||||
✅ **分层测试支持**
|
||||
✅ **丰富的测试信息**
|
||||
✅ **历史趋势分析**
|
||||
✅ **CI/CD集成**
|
||||
|
||||
通过合理使用Allure报告,可以:
|
||||
- 快速定位测试失败原因
|
||||
- 追踪测试稳定性
|
||||
- 分析测试性能
|
||||
- 提升测试质量
|
||||
@@ -0,0 +1,513 @@
|
||||
# Lighthouse CI使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
Lighthouse CI是一个用于自动化性能测试的工具,它可以在CI/CD流程中运行Lighthouse审计,跟踪性能指标变化,并设置性能预算。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
npm install --save-dev @lhci/cli
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### 配置文件
|
||||
|
||||
项目根目录下的`lighthouserc.json`文件包含所有配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"numberOfRuns": 3,
|
||||
"startServerCommand": "npm run start",
|
||||
"startServerReadyPattern": "Local:",
|
||||
"url": [
|
||||
"http://localhost:3000/",
|
||||
"http://localhost:3000/about",
|
||||
"http://localhost:3000/services"
|
||||
],
|
||||
"settings": {
|
||||
"preset": "desktop",
|
||||
"onlyCategories": [
|
||||
"performance",
|
||||
"accessibility",
|
||||
"best-practices",
|
||||
"seo"
|
||||
]
|
||||
}
|
||||
},
|
||||
"assert": {
|
||||
"assertions": {
|
||||
"categories:performance": ["error", {"minScore": 0.9}],
|
||||
"categories:accessibility": ["error", {"minScore": 0.9}],
|
||||
"categories:best-practices": ["error", {"minScore": 0.9}],
|
||||
"categories:seo": ["error", {"minScore": 0.9}]
|
||||
}
|
||||
},
|
||||
"upload": {
|
||||
"target": "temporary-public-storage"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 配置说明
|
||||
|
||||
#### collect配置
|
||||
|
||||
- **numberOfRuns**: 每个URL运行的次数(默认3次)
|
||||
- **startServerCommand**: 启动服务器的命令
|
||||
- **startServerReadyPattern**: 服务器就绪的匹配模式
|
||||
- **url**: 要测试的URL列表
|
||||
- **settings**: Lighthouse设置
|
||||
- **preset**: 测试预设(desktop/mobile)
|
||||
- **onlyCategories**: 只测试指定类别
|
||||
|
||||
#### assert配置
|
||||
|
||||
- **assertions**: 性能断言
|
||||
- **categories:performance**: 性能分数最低0.9
|
||||
- **categories:accessibility**: 可访问性分数最低0.9
|
||||
- **categories:best-practices**: 最佳实践分数最低0.9
|
||||
- **categories:seo**: SEO分数最低0.9
|
||||
|
||||
#### upload配置
|
||||
|
||||
- **target**: 上传目标
|
||||
- **temporary-public-storage**: 临时公共存储
|
||||
- **lhci**: Lighthouse CI服务器
|
||||
- **filesystem**: 本地文件系统
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 本地运行
|
||||
|
||||
#### 运行完整测试
|
||||
|
||||
```bash
|
||||
npm run lighthouse
|
||||
```
|
||||
|
||||
这将执行以下步骤:
|
||||
1. 启动开发服务器
|
||||
2. 收集性能数据
|
||||
3. 运行断言检查
|
||||
4. 上传结果
|
||||
|
||||
#### 分步运行
|
||||
|
||||
```bash
|
||||
# 1. 收集性能数据
|
||||
npm run lighthouse:collect
|
||||
|
||||
# 2. 运行断言检查
|
||||
npm run lighthouse:assert
|
||||
|
||||
# 3. 上传结果
|
||||
npm run lighthouse:upload
|
||||
```
|
||||
|
||||
#### 指定设备类型
|
||||
|
||||
```bash
|
||||
# 桌面端测试
|
||||
npm run lighthouse:desktop
|
||||
|
||||
# 移动端测试
|
||||
npm run lighthouse:mobile
|
||||
```
|
||||
|
||||
### CI/CD集成
|
||||
|
||||
#### GitHub Actions
|
||||
|
||||
创建`.github/workflows/lighthouse.yml`:
|
||||
|
||||
```yaml
|
||||
name: Lighthouse CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lighthouse:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build application
|
||||
run: npm run build
|
||||
|
||||
- name: Run Lighthouse CI
|
||||
run: npm run lighthouse
|
||||
env:
|
||||
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
|
||||
|
||||
- name: Upload Lighthouse results
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: lighthouse-results
|
||||
path: lighthouse-reports
|
||||
```
|
||||
|
||||
#### GitLab CI
|
||||
|
||||
创建`.gitlab-ci.yml`:
|
||||
|
||||
```yaml
|
||||
lighthouse:
|
||||
stage: test
|
||||
image: node:18
|
||||
script:
|
||||
- npm ci
|
||||
- npm run build
|
||||
- npm run lighthouse
|
||||
artifacts:
|
||||
paths:
|
||||
- lighthouse-reports/
|
||||
expire_in: 1 week
|
||||
only:
|
||||
- main
|
||||
- merge_requests
|
||||
```
|
||||
|
||||
## 性能指标
|
||||
|
||||
### Core Web Vitals
|
||||
|
||||
Lighthouse CI重点监控以下Core Web Vitals指标:
|
||||
|
||||
| 指标 | 名称 | 目标值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| **LCP** | Largest Contentful Paint | < 2.5s | 最大内容绘制时间 |
|
||||
| **FID** | First Input Delay | < 100ms | 首次输入延迟 |
|
||||
| **CLS** | Cumulative Layout Shift | < 0.1 | 累积布局偏移 |
|
||||
|
||||
### 其他重要指标
|
||||
|
||||
| 指标 | 名称 | 目标值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| **FCP** | First Contentful Paint | < 1.8s | 首次内容绘制时间 |
|
||||
| **SI** | Speed Index | < 3.4s | 速度指数 |
|
||||
| **TBT** | Total Blocking Time | < 200ms | 总阻塞时间 |
|
||||
|
||||
### 性能预算
|
||||
|
||||
在`lighthouserc.json`中设置性能预算:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"assert": {
|
||||
"assertions": {
|
||||
"first-contentful-paint": ["error", {"maxNumericValue": 2000}],
|
||||
"largest-contentful-paint": ["error", {"maxNumericValue": 3000}],
|
||||
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
|
||||
"total-blocking-time": ["error", {"maxNumericValue": 300}],
|
||||
"speed-index": ["error", {"maxNumericValue": 3000}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 减少LCP时间
|
||||
|
||||
**问题**:LCP > 2.5s
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 优化图片加载
|
||||
<img
|
||||
src="/hero.jpg"
|
||||
alt="Hero image"
|
||||
loading="eager"
|
||||
fetchpriority="high"
|
||||
/>
|
||||
|
||||
// 使用Next.js Image组件
|
||||
<Image
|
||||
src="/hero.jpg"
|
||||
alt="Hero image"
|
||||
priority
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
```
|
||||
|
||||
### 2. 减少CLS
|
||||
|
||||
**问题**:CLS > 0.1
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 为图片预留空间
|
||||
<img
|
||||
src="/image.jpg"
|
||||
alt="Description"
|
||||
width={800}
|
||||
height={600}
|
||||
style={{ aspectRatio: '800/600' }}
|
||||
/>
|
||||
|
||||
// 避免内容跳动
|
||||
<div style={{ minHeight: '200px' }}>
|
||||
{loading ? <Skeleton /> : <Content />}
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. 减少TBT
|
||||
|
||||
**问题**:TBT > 200ms
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 代码分割
|
||||
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
|
||||
loading: () => <Loading />,
|
||||
});
|
||||
|
||||
// 延迟加载非关键脚本
|
||||
useEffect(() => {
|
||||
import('heavy-library').then(module => {
|
||||
// 使用库
|
||||
});
|
||||
}, []);
|
||||
```
|
||||
|
||||
### 4. 优化FCP
|
||||
|
||||
**问题**:FCP > 1.8s
|
||||
|
||||
**解决方案**:
|
||||
```typescript
|
||||
// 内联关键CSS
|
||||
<style dangerouslySetInnerHTML={{__html: criticalCSS}} />
|
||||
|
||||
// 预加载关键资源
|
||||
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossOrigin="anonymous" />
|
||||
|
||||
// 减少阻塞资源
|
||||
<script src="/script.js" defer></script>
|
||||
```
|
||||
|
||||
## 查看报告
|
||||
|
||||
### 本地报告
|
||||
|
||||
运行测试后,报告保存在`lighthouse-reports/`目录:
|
||||
|
||||
```bash
|
||||
# 打开最新报告
|
||||
open lighthouse-reports/lhci-*.html
|
||||
```
|
||||
|
||||
### 在线报告
|
||||
|
||||
如果使用临时公共存储,测试后会输出报告URL:
|
||||
|
||||
```
|
||||
✅ Report: https://storage.googleapis.com/lighthouse-infrastructure.appspot.com/reports/xxxxx.html
|
||||
```
|
||||
|
||||
### Lighthouse CI服务器
|
||||
|
||||
配置Lighthouse CI服务器后,可以查看历史趋势:
|
||||
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"upload": {
|
||||
"target": "lhci",
|
||||
"serverBaseUrl": "https://your-lhci-server.com",
|
||||
"token": "your-upload-token"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能监控仪表板
|
||||
|
||||
### 创建自定义仪表板
|
||||
|
||||
```typescript
|
||||
// scripts/performance-dashboard.ts
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
interface LighthouseResult {
|
||||
url: string;
|
||||
performance: number;
|
||||
accessibility: number;
|
||||
'best-practices': number;
|
||||
seo: number;
|
||||
lcp: number;
|
||||
fcp: number;
|
||||
cls: number;
|
||||
tbt: number;
|
||||
}
|
||||
|
||||
function generateDashboard(results: LighthouseResult[]) {
|
||||
const avgPerformance = results.reduce((sum, r) => sum + r.performance, 0) / results.length;
|
||||
const avgAccessibility = results.reduce((sum, r) => sum + r.accessibility, 0) / results.length;
|
||||
|
||||
console.log(`
|
||||
## 性能监控仪表板
|
||||
|
||||
### 平均分数
|
||||
- 性能: ${(avgPerformance * 100).toFixed(0)}/100
|
||||
- 可访问性: ${(avgAccessibility * 100).toFixed(0)}/100
|
||||
|
||||
### 各页面性能
|
||||
${results.map(r => `
|
||||
#### ${r.url}
|
||||
- 性能: ${(r.performance * 100).toFixed(0)}
|
||||
- LCP: ${r.lcp}ms
|
||||
- FCP: ${r.fcp}ms
|
||||
- CLS: ${r.cls}
|
||||
`).join('\n')}
|
||||
`);
|
||||
}
|
||||
|
||||
// 读取结果并生成仪表板
|
||||
const resultsPath = path.join(process.cwd(), 'lighthouse-reports', 'manifest.json');
|
||||
const results = JSON.parse(fs.readFileSync(resultsPath, 'utf-8'));
|
||||
generateDashboard(results);
|
||||
```
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 问题1:服务器启动失败
|
||||
|
||||
**症状**:`Error: Server did not start in time`
|
||||
|
||||
**解决方案**:
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"startServerReadyPattern": "Local:",
|
||||
"startServerReadyTimeout": 60000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题2:性能分数不达标
|
||||
|
||||
**症状**:`Assertion failed: categories:performance`
|
||||
|
||||
**解决方案**:
|
||||
1. 查看详细报告找出问题
|
||||
2. 根据优化建议进行改进
|
||||
3. 调整性能预算(临时)
|
||||
|
||||
```json
|
||||
{
|
||||
"assertions": {
|
||||
"categories:performance": ["warn", {"minScore": 0.8}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题3:测试超时
|
||||
|
||||
**症状**:`Navigation timeout of 30000 ms exceeded`
|
||||
|
||||
**解决方案**:
|
||||
```json
|
||||
{
|
||||
"ci": {
|
||||
"collect": {
|
||||
"settings": {
|
||||
"maxWaitForLoad": 45000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### ✅ 推荐做法
|
||||
|
||||
1. **设置合理的性能预算**
|
||||
- 基于当前性能设置目标
|
||||
- 逐步提高标准
|
||||
|
||||
2. **定期运行测试**
|
||||
- 在CI/CD中自动运行
|
||||
- 每次部署前检查
|
||||
|
||||
3. **监控性能趋势**
|
||||
- 使用Lighthouse CI服务器
|
||||
- 跟踪性能变化
|
||||
|
||||
4. **优化关键页面**
|
||||
- 首页
|
||||
- 高流量页面
|
||||
- 转化页面
|
||||
|
||||
### ❌ 避免的做法
|
||||
|
||||
1. **不要设置过高的目标**
|
||||
```json
|
||||
// ❌ 不现实
|
||||
"categories:performance": ["error", {"minScore": 1.0}]
|
||||
|
||||
// ✅ 合理
|
||||
"categories:performance": ["error", {"minScore": 0.9}]
|
||||
```
|
||||
|
||||
2. **不要忽略移动端性能**
|
||||
```bash
|
||||
# 同时测试桌面和移动端
|
||||
npm run lighthouse:desktop
|
||||
npm run lighthouse:mobile
|
||||
```
|
||||
|
||||
3. **不要跳过性能测试**
|
||||
- 性能问题会影响用户体验
|
||||
- 早期发现问题更容易修复
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Lighthouse CI官方文档](https://github.com/GoogleChrome/lighthouse-ci)
|
||||
- [Web Vitals](https://web.dev/vitals/)
|
||||
- [性能优化指南](https://web.dev/performance/)
|
||||
- [Lighthouse评分指南](https://web.dev/performance-scoring/)
|
||||
|
||||
## 总结
|
||||
|
||||
Lighthouse CI已完全集成到项目中,提供了:
|
||||
|
||||
✅ **自动化性能测试**
|
||||
✅ **性能预算监控**
|
||||
✅ **CI/CD集成**
|
||||
✅ **详细的性能报告**
|
||||
✅ **历史趋势跟踪**
|
||||
|
||||
通过合理使用Lighthouse CI,可以:
|
||||
- 保持良好的性能水平
|
||||
- 及时发现性能退化
|
||||
- 持续优化用户体验
|
||||
- 建立性能文化
|
||||
@@ -0,0 +1,213 @@
|
||||
# 单元测试覆盖率改进计划
|
||||
|
||||
## 当前状态
|
||||
|
||||
**测试日期**: 2026-03-20
|
||||
|
||||
### 当前覆盖率
|
||||
|
||||
| 指标 | 当前值 | 目标值 | 差距 |
|
||||
|------|--------|--------|------|
|
||||
| **Lines** | 29.36% | 80% | -50.64% |
|
||||
| **Statements** | 29.5% | 80% | -50.5% |
|
||||
| **Functions** | 30.21% | 80% | -49.79% |
|
||||
| **Branches** | 22.66% | 80% | -57.34% |
|
||||
|
||||
### 当前阈值设置
|
||||
|
||||
```javascript
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 35,
|
||||
functions: 45,
|
||||
lines: 45,
|
||||
statements: 45,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 改进策略
|
||||
|
||||
### 阶段一:快速提升(当前 → 50%)
|
||||
|
||||
**时间**: 2周
|
||||
|
||||
**目标**: 将覆盖率从当前水平提升到50%
|
||||
|
||||
**重点区域**:
|
||||
1. **核心业务逻辑**
|
||||
- [ ] `src/app/(marketing)/contact/actions.ts` (当前: 0%)
|
||||
- [ ] `src/app/api/admin/content/route.ts` (当前: 0%)
|
||||
- [ ] `src/app/api/admin/users/route.ts` (当前: 0%)
|
||||
|
||||
2. **工具函数**
|
||||
- [ ] `src/lib/sanitize.ts`
|
||||
- [ ] `src/lib/csrf.ts`
|
||||
- [ ] `src/lib/constants.ts`
|
||||
|
||||
3. **Hooks**
|
||||
- [ ] `src/hooks/use-media-query.ts`
|
||||
- [ ] `src/hooks/use-scroll-reveal.ts`
|
||||
- [ ] `src/hooks/use-intersection-observer.ts`
|
||||
|
||||
**行动项**:
|
||||
- 为每个核心函数编写基础测试用例
|
||||
- 覆盖主要成功路径和常见错误场景
|
||||
- 使用Mock隔离外部依赖
|
||||
|
||||
### 阶段二:稳步推进(50% → 65%)
|
||||
|
||||
**时间**: 3周
|
||||
|
||||
**目标**: 将覆盖率从50%提升到65%
|
||||
|
||||
**重点区域**:
|
||||
1. **UI组件**
|
||||
- [ ] `src/components/ui/button.tsx`
|
||||
- [ ] `src/components/ui/input.tsx`
|
||||
- [ ] `src/components/ui/textarea.tsx`
|
||||
- [ ] `src/components/ui/toast.tsx`
|
||||
|
||||
2. **页面组件**
|
||||
- [ ] `src/app/(marketing)/about/page.tsx`
|
||||
- [ ] `src/app/(marketing)/products/page.tsx`
|
||||
- [ ] `src/app/(marketing)/services/page.tsx`
|
||||
|
||||
**行动项**:
|
||||
- 使用React Testing Library测试组件交互
|
||||
- 测试用户事件(点击、输入、提交)
|
||||
- 测试组件状态变化和副作用
|
||||
|
||||
### 阶段三:精细打磨(65% → 80%)
|
||||
|
||||
**时间**: 4周
|
||||
|
||||
**目标**: 将覆盖率从65%提升到80%
|
||||
|
||||
**重点区域**:
|
||||
1. **边界情况**
|
||||
- [ ] 错误处理逻辑
|
||||
- [ ] 空值/undefined处理
|
||||
- [ ] 异常输入验证
|
||||
|
||||
2. **复杂场景**
|
||||
- [ ] API集成测试
|
||||
- [ ] 数据库交互测试
|
||||
- [ ] 认证/授权流程
|
||||
|
||||
**行动项**:
|
||||
- 补充边界测试用例
|
||||
- 测试错误处理和异常流程
|
||||
- 使用MSW (Mock Service Worker)测试API调用
|
||||
- 使用Testcontainers测试数据库交互
|
||||
|
||||
## 执行计划
|
||||
|
||||
### 每周任务分配
|
||||
|
||||
#### Week 1-2: 核心业务逻辑
|
||||
- [ ] 联系表单Server Actions测试
|
||||
- [ ] 内容管理API测试
|
||||
- [ ] 用户管理API测试
|
||||
- [ ] 工具函数测试
|
||||
|
||||
#### Week 3-4: UI组件基础
|
||||
- [ ] 表单组件测试
|
||||
- [ ] 按钮组件测试
|
||||
- [ ] Toast组件测试
|
||||
- [ ] 基础页面组件测试
|
||||
|
||||
#### Week 5-7: 页面组件
|
||||
- [ ] 营销页面测试
|
||||
- [ ] 产品页面测试
|
||||
- [ ] 服务页面测试
|
||||
- [ ] 案例页面测试
|
||||
|
||||
#### Week 8-9: 边界情况
|
||||
- [ ] 错误处理测试
|
||||
- [ ] 边界值测试
|
||||
- [ ] 异常流程测试
|
||||
|
||||
## 工具和资源
|
||||
|
||||
### 测试工具
|
||||
- **Jest**: 单元测试框架
|
||||
- **React Testing Library**: React组件测试
|
||||
- **MSW**: API Mock
|
||||
- **Testcontainers**: 数据库集成测试
|
||||
|
||||
### 覆盖率报告
|
||||
```bash
|
||||
# 生成覆盖率报告
|
||||
npm run test:coverage
|
||||
|
||||
# 查看HTML报告
|
||||
open coverage/lcov-report/index.html
|
||||
```
|
||||
|
||||
### CI集成
|
||||
```yaml
|
||||
# GitHub Actions示例
|
||||
- name: Run tests with coverage
|
||||
run: npm run test:coverage:check
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage/coverage-final.json
|
||||
```
|
||||
|
||||
## 验收标准
|
||||
|
||||
### 阶段一完成标准
|
||||
- [ ] 核心业务逻辑覆盖率 ≥ 70%
|
||||
- [ ] 工具函数覆盖率 ≥ 80%
|
||||
- [ ] Hooks覆盖率 ≥ 60%
|
||||
- [ ] 总体覆盖率 ≥ 50%
|
||||
|
||||
### 阶段二完成标准
|
||||
- [ ] UI组件覆盖率 ≥ 60%
|
||||
- [ ] 页面组件覆盖率 ≥ 50%
|
||||
- [ ] 总体覆盖率 ≥ 65%
|
||||
|
||||
### 阶段三完成标准
|
||||
- [ ] 所有模块覆盖率 ≥ 75%
|
||||
- [ ] 总体覆盖率 ≥ 80%
|
||||
- [ ] CI中强制执行覆盖率阈值
|
||||
|
||||
## 持续改进
|
||||
|
||||
### 定期审查
|
||||
- 每周审查覆盖率报告
|
||||
- 识别低覆盖率模块
|
||||
- 优先测试高影响区域
|
||||
|
||||
### 新代码要求
|
||||
- 所有新代码必须包含单元测试
|
||||
- 新代码覆盖率要求 ≥ 80%
|
||||
- PR必须通过覆盖率检查
|
||||
|
||||
### 重构支持
|
||||
- 重构前确保有足够的测试覆盖
|
||||
- 重构后验证测试仍然通过
|
||||
- 利用测试作为重构的安全网
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **不要为了覆盖率而写测试**
|
||||
- 测试应该验证行为,而不是代码行
|
||||
- 有意义的测试比高覆盖率更重要
|
||||
|
||||
2. **关注关键路径**
|
||||
- 优先测试核心业务逻辑
|
||||
- 确保关键功能有充分的测试覆盖
|
||||
|
||||
3. **保持测试质量**
|
||||
- 测试代码也需要维护
|
||||
- 定期清理和重构测试代码
|
||||
- 避免脆弱的测试
|
||||
|
||||
4. **平衡投入产出**
|
||||
- 80%覆盖率是目标,不是硬性要求
|
||||
- 某些代码可能不需要测试(如简单的getter/setter)
|
||||
- 根据风险和重要性分配测试资源
|
||||
@@ -0,0 +1,310 @@
|
||||
# 分层测试优化指南
|
||||
|
||||
## 概述
|
||||
|
||||
本文档介绍如何使用分层测试系统来优化测试执行效率,缩短测试时间,提高测试质量。
|
||||
|
||||
## 测试层级
|
||||
|
||||
### 快速层 (Fast Tier)
|
||||
|
||||
**特点:**
|
||||
- 执行时间:30秒内
|
||||
- 测试类型:冒烟测试、API测试、基础功能验证
|
||||
- 并行度:高(75% workers)
|
||||
- 失败策略:快速失败(failFast: true)
|
||||
- 重试次数:1次
|
||||
|
||||
**适用场景:**
|
||||
- 每次代码提交后的快速验证
|
||||
- 持续集成(CI)中的第一道防线
|
||||
- 关键路径的功能验证
|
||||
|
||||
**示例:**
|
||||
```typescript
|
||||
test.describe('API快速测试 @smoke @critical', () => {
|
||||
test('应该能够获取内容列表', async ({ request }) => {
|
||||
const response = await request.get('/api/admin/content');
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 标准层 (Standard Tier)
|
||||
|
||||
**特点:**
|
||||
- 执行时间:60秒内
|
||||
- 测试类型:功能测试、响应式测试、移动端核心功能
|
||||
- 并行度:中(50% workers)
|
||||
- 失败策略:继续执行(failFast: false)
|
||||
- 重试次数:2次
|
||||
|
||||
**适用场景:**
|
||||
- 功能分支合并前的验证
|
||||
- 回归测试的主要部分
|
||||
- 跨浏览器/设备测试
|
||||
|
||||
**示例:**
|
||||
```typescript
|
||||
test.describe('管理后台功能测试 @admin @regression', () => {
|
||||
test('应该能够创建和编辑新闻', async ({ page }) => {
|
||||
await page.goto('/admin/news');
|
||||
await page.click('[data-testid="create-news-btn"]');
|
||||
// ... 测试逻辑
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 深度层 (Deep Tier)
|
||||
|
||||
**特点:**
|
||||
- 执行时间:120秒内
|
||||
- 测试类型:视觉回归、性能测试、完整回归测试
|
||||
- 并行度:低(25% workers)
|
||||
- 失败策略:继续执行(failFast: false)
|
||||
- 重试次数:3次
|
||||
|
||||
**适用场景:**
|
||||
- 发布前的完整验证
|
||||
- 夜间/周末的全面回归
|
||||
- 性能和视觉质量检查
|
||||
|
||||
**示例:**
|
||||
```typescript
|
||||
test.describe('首页视觉回归测试 @visual @regression', () => {
|
||||
test('首页应该与基准截图一致', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await expect(page).toHaveScreenshot('homepage.png');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 本地开发
|
||||
|
||||
#### 运行快速层测试
|
||||
```bash
|
||||
npm run test:tier:fast
|
||||
```
|
||||
|
||||
#### 运行标准层测试
|
||||
```bash
|
||||
npm run test:tier:standard
|
||||
```
|
||||
|
||||
#### 运行深度层测试
|
||||
```bash
|
||||
npm run test:tier:deep
|
||||
```
|
||||
|
||||
#### 运行所有层级测试
|
||||
```bash
|
||||
npm run test:tier:all
|
||||
```
|
||||
|
||||
### CI/CD集成
|
||||
|
||||
#### Woodpecker CI配置
|
||||
|
||||
项目已配置Woodpecker CI工作流,支持分层测试自动化执行:
|
||||
|
||||
1. **完整工作流** (`.woodpecker/test-tiered.yml`)
|
||||
- 依次执行快速层、标准层、深度层
|
||||
- 前一层失败则停止后续执行
|
||||
- 生成测试报告并上传
|
||||
- 发送通知
|
||||
|
||||
2. **简化工作流** (`.woodpecker/test-tiered-simple.yml`)
|
||||
- 根据分支类型执行不同层级
|
||||
- main分支:执行所有层级
|
||||
- develop分支:执行快速层和标准层
|
||||
- 其他分支:仅执行快速层
|
||||
|
||||
### 测试标记
|
||||
|
||||
使用测试标记来分类和管理测试:
|
||||
|
||||
```typescript
|
||||
test.describe('测试套件 @smoke @critical', () => {
|
||||
test('测试用例 @api @regression', async ({ page }) => {
|
||||
// 测试逻辑
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**可用标记:**
|
||||
- `@smoke` - 冒烟测试
|
||||
- `@critical` - 关键测试
|
||||
- `@regression` - 回归测试
|
||||
- `@visual` - 视觉测试
|
||||
- `@performance` - 性能测试
|
||||
- `@api` - API测试
|
||||
- `@mobile` - 移动端测试
|
||||
- `@responsive` - 响应式测试
|
||||
- `@admin` - 管理后台测试
|
||||
- `@a11y` - 可访问性测试
|
||||
- `@security` - 安全测试
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 识别慢速测试
|
||||
|
||||
使用性能优化工具分析测试执行时间:
|
||||
|
||||
```bash
|
||||
cd e2e && node test-optimizer-simple-test.js
|
||||
```
|
||||
|
||||
工具会生成优化报告,包含:
|
||||
- 慢速测试列表
|
||||
- 优化建议
|
||||
- 潜在时间节省
|
||||
|
||||
### 优化建议
|
||||
|
||||
#### 1. 减少等待时间
|
||||
```typescript
|
||||
// 不推荐
|
||||
await page.waitForTimeout(5000);
|
||||
|
||||
// 推荐
|
||||
await page.waitForSelector('[data-testid="result"]', { timeout: 5000 });
|
||||
```
|
||||
|
||||
#### 2. 优化选择器
|
||||
```typescript
|
||||
// 不推荐
|
||||
await page.click('div > div > button');
|
||||
|
||||
// 推荐
|
||||
await page.click('[data-testid="submit-btn"]');
|
||||
```
|
||||
|
||||
#### 3. 并行测试
|
||||
```typescript
|
||||
// playwright.config.tiered.ts
|
||||
{
|
||||
fullyParallel: true, // 启用并行执行
|
||||
workers: '75%', // 使用75%的CPU核心
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. 测试拆分
|
||||
```typescript
|
||||
// 不推荐:单个大测试
|
||||
test('完整的用户注册流程', async ({ page }) => {
|
||||
await page.goto('/register');
|
||||
await page.fill('[name="email"]', 'test@example.com');
|
||||
await page.fill('[name="password"]', 'password123');
|
||||
await page.fill('[name="confirm"]', 'password123');
|
||||
await page.click('[type="submit"]');
|
||||
await page.waitForURL('/dashboard');
|
||||
await page.click('[data-testid="profile"]');
|
||||
// ... 更多步骤
|
||||
});
|
||||
|
||||
// 推荐:拆分为多个小测试
|
||||
test.describe('用户注册流程', () => {
|
||||
test('应该能够填写注册表单', async ({ page }) => {
|
||||
await page.goto('/register');
|
||||
await page.fill('[name="email"]', 'test@example.com');
|
||||
await page.fill('[name="password"]', 'password123');
|
||||
await page.fill('[name="confirm"]', 'password123');
|
||||
});
|
||||
|
||||
test('应该能够提交注册', async ({ page }) => {
|
||||
await page.goto('/register');
|
||||
await page.fill('[name="email"]', 'test@example.com');
|
||||
await page.fill('[name="password"]', 'password123');
|
||||
await page.fill('[name="confirm"]', 'password123');
|
||||
await page.click('[type="submit"]');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 监控和告警
|
||||
|
||||
### 测试执行历史
|
||||
|
||||
系统会自动记录每次测试执行的历史数据,包括:
|
||||
- 执行时间
|
||||
- 成功/失败状态
|
||||
- 测试标记
|
||||
|
||||
历史数据存储在 `e2e/test-history.json`。
|
||||
|
||||
### 告警规则
|
||||
|
||||
系统会根据以下规则触发告警:
|
||||
|
||||
1. **测试通过率低于80%** (Critical)
|
||||
2. **测试通过率低于90%** (High)
|
||||
3. **测试执行时间超过30分钟** (Medium)
|
||||
4. **失败测试数量超过10个** (High)
|
||||
5. **深度层测试存在失败** (Critical)
|
||||
|
||||
### 查看告警
|
||||
|
||||
告警信息会输出到控制台,并保存在 `test-results/alerts.json`。
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 测试分层原则
|
||||
|
||||
- **快速层**:只包含最关键的测试,确保在5分钟内完成
|
||||
- **标准层**:包含大部分功能测试,确保在30分钟内完成
|
||||
- **深度层**:包含完整的回归测试,可以接受较长的执行时间
|
||||
|
||||
### 2. 测试标记使用
|
||||
|
||||
- 为每个测试套件添加合适的标记
|
||||
- 优先使用 `@smoke` 和 `@critical` 标记关键测试
|
||||
- 使用 `@regression` 标记需要定期运行的测试
|
||||
|
||||
### 3. 持续优化
|
||||
|
||||
- 定期运行性能优化工具
|
||||
- 关注慢速测试的优化
|
||||
- 根据历史数据调整测试分层
|
||||
|
||||
### 4. CI/CD集成
|
||||
|
||||
- 在每次提交时运行快速层测试
|
||||
- 在合并PR时运行标准层测试
|
||||
- 在发布前运行深度层测试
|
||||
|
||||
## 故障排查
|
||||
|
||||
### 测试超时
|
||||
|
||||
**问题:** 测试执行超时
|
||||
|
||||
**解决方案:**
|
||||
1. 检查是否有不必要的等待
|
||||
2. 增加测试超时时间
|
||||
3. 检查网络请求是否正常
|
||||
|
||||
### 测试不稳定
|
||||
|
||||
**问题:** 测试时好时坏
|
||||
|
||||
**解决方案:**
|
||||
1. 增加重试次数
|
||||
2. 使用更稳定的等待策略
|
||||
3. 检查是否有竞态条件
|
||||
|
||||
### 执行时间过长
|
||||
|
||||
**问题:** 测试执行时间超过预期
|
||||
|
||||
**解决方案:**
|
||||
1. 运行性能优化工具
|
||||
2. 检查是否有慢速测试
|
||||
3. 考虑调整测试分层
|
||||
|
||||
## 参考资源
|
||||
|
||||
- [Playwright文档](https://playwright.dev/)
|
||||
- [测试金字塔](https://martinfowler.com/articles/practical-test-pyramid.html)
|
||||
- [Woodpecker CI文档](https://woodpecker-ci.org/docs/)
|
||||
@@ -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)
|
||||
@@ -0,0 +1,28 @@
|
||||
# 测试指南
|
||||
|
||||
## 测试策略
|
||||
|
||||
本项目采用分层覆盖率策略:
|
||||
- 核心业务逻辑层:70-80%覆盖率
|
||||
- UI组件层:60-70%覆盖率
|
||||
- 页面展示层:40-50%覆盖率
|
||||
|
||||
## 测试编写规范
|
||||
|
||||
### 单元测试
|
||||
- 使用Jest和React Testing Library
|
||||
- 遵循AAA模式(Arrange-Act-Assert)
|
||||
- 每个测试只验证一个行为
|
||||
|
||||
### 集成测试
|
||||
- 测试组件间的交互
|
||||
- 使用真实的数据流
|
||||
- 避免过度mock
|
||||
|
||||
## 常见问题
|
||||
|
||||
**Q: 如何处理异步测试?**
|
||||
A: 使用async/await和waitFor函数。
|
||||
|
||||
**Q: 如何测试错误处理?**
|
||||
A: 使用toThrow和expect.assertions验证错误路径。
|
||||
@@ -0,0 +1,605 @@
|
||||
# 测试文档
|
||||
|
||||
## 测试概述
|
||||
|
||||
项目使用 Playwright 进行端到端(E2E)测试,测试框架位于 `e2e/` 目录,采用 Page Object 模式组织测试代码。
|
||||
|
||||
## 测试框架结构
|
||||
|
||||
```
|
||||
e2e/
|
||||
├── src/
|
||||
│ ├── config/ # 配置文件
|
||||
│ │ ├── environments.ts # 环境配置
|
||||
│ │ └── network-configs.ts # 网络配置
|
||||
│ ├── data/ # 测试数据
|
||||
│ │ └── test-data.ts
|
||||
│ ├── fixtures/ # 测试 Fixtures
|
||||
│ │ ├── base.fixture.ts # 基础 Fixture
|
||||
│ │ └── a11y.fixture.ts # 可访问性 Fixture
|
||||
│ ├── pages/ # Page Object Model
|
||||
│ │ ├── BasePage.ts # 基础页面
|
||||
│ │ ├── HomePage.ts # 首页
|
||||
│ │ ├── AboutPage.ts # 关于页
|
||||
│ │ ├── CasesPage.ts # 案例页
|
||||
│ │ ├── ContactPage.ts # 联系页
|
||||
│ │ ├── NewsPage.ts # 新闻页
|
||||
│ │ ├── ProductsPage.ts # 产品页
|
||||
│ │ ├── ServicesPage.ts # 服务页
|
||||
│ │ └── SolutionsPage.ts # 解决方案页
|
||||
│ └── tests/ # 测试用例
|
||||
│ ├── accessibility/ # 可访问性测试
|
||||
│ ├── debug/ # 调试测试
|
||||
│ ├── deployment/ # 部署就绪测试
|
||||
│ ├── error-handling/ # 错误处理测试
|
||||
│ ├── mobile/ # 移动端测试
|
||||
│ ├── performance/ # 性能测试
|
||||
│ ├── regression/ # 回归测试
|
||||
│ ├── responsive/ # 响应式测试
|
||||
│ ├── security/ # 安全测试
|
||||
│ ├── smoke/ # 冒烟测试
|
||||
│ ├── utils/ # 工具测试
|
||||
│ └── visual/ # 视觉回归测试
|
||||
├── playwright.config.ts # Playwright 配置
|
||||
├── package.json
|
||||
└── .env.example
|
||||
```
|
||||
|
||||
## 测试类型
|
||||
|
||||
### 1. 冒烟测试 (Smoke Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/smoke/`
|
||||
|
||||
**目的**: 验证核心功能是否正常工作
|
||||
|
||||
**测试文件**:
|
||||
- `all-pages.spec.ts` - 所有页面加载测试
|
||||
- `home-page.smoke.spec.ts` - 首页冒烟测试
|
||||
- `contact-page.smoke.spec.ts` - 联系页冒烟测试
|
||||
- `navigation.smoke.spec.ts` - 导航冒烟测试
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:smoke
|
||||
# 或
|
||||
npx playwright test --grep @smoke
|
||||
```
|
||||
|
||||
### 2. 回归测试 (Regression Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/regression/`
|
||||
|
||||
**目的**: 确保新代码没有破坏现有功能
|
||||
|
||||
**测试文件**:
|
||||
- `contact-form.regression.spec.ts` - 联系表单回归测试
|
||||
- `home-page.regression.spec.ts` - 首页回归测试
|
||||
- `navigation.spec.ts` - 导航回归测试
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:regression
|
||||
# 或
|
||||
npx playwright test --grep @regression
|
||||
```
|
||||
|
||||
### 3. 性能测试 (Performance Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/performance/`
|
||||
|
||||
**目的**: 验证页面性能指标
|
||||
|
||||
**测试文件**:
|
||||
- `core-web-vitals.spec.ts` - Core Web Vitals 测试
|
||||
- `performance.spec.ts` - 通用性能测试
|
||||
- `image-loading.spec.ts` - 图片加载性能
|
||||
- `interaction-performance.spec.ts` - 交互性能测试
|
||||
|
||||
**监控指标**:
|
||||
- LCP (Largest Contentful Paint) < 2.5s
|
||||
- FID (First Input Delay) < 100ms
|
||||
- CLS (Cumulative Layout Shift) < 0.1
|
||||
- TTFB (Time to First Byte) < 600ms
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:performance
|
||||
# 或
|
||||
npx playwright test --grep @performance
|
||||
```
|
||||
|
||||
### 4. 响应式测试 (Responsive Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/responsive/`
|
||||
|
||||
**目的**: 验证多设备适配
|
||||
|
||||
**测试文件**:
|
||||
- `responsive.spec.ts` - 响应式布局测试
|
||||
- `mobile-interaction.spec.ts` - 移动端交互测试
|
||||
|
||||
**测试设备**:
|
||||
- Desktop: 1920x1080, 1366x768
|
||||
- Tablet: iPad Pro (1024x1366), iPad Air (820x1180)
|
||||
- Mobile: iPhone 12 (390x844), iPhone SE (375x667), Pixel 5 (393x851)
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:responsive
|
||||
# 或
|
||||
npx playwright test --grep @responsive
|
||||
```
|
||||
|
||||
### 5. 可访问性测试 (Accessibility Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/accessibility/`
|
||||
|
||||
**目的**: 验证 WCAG 合规性
|
||||
|
||||
**测试文件**:
|
||||
- `accessibility.spec.ts` - 可访问性测试
|
||||
- `wcag-compliance.spec.ts` - WCAG 合规测试
|
||||
|
||||
**检查项**:
|
||||
- 颜色对比度 ≥ 4.5:1 (AA 级别)
|
||||
- 键盘导航支持
|
||||
- 屏幕阅读器兼容
|
||||
- ARIA 属性正确性
|
||||
- 焦点顺序合理
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:accessibility
|
||||
# 或
|
||||
npx playwright test --grep @accessibility
|
||||
```
|
||||
|
||||
### 6. 安全测试 (Security Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/security/`
|
||||
|
||||
**目的**: 验证安全防护措施
|
||||
|
||||
**测试文件**:
|
||||
- `security.spec.ts` - 通用安全测试
|
||||
- `xss-protection.spec.ts` - XSS 防护测试
|
||||
- `csrf-protection.spec.ts` - CSRF 防护测试
|
||||
|
||||
**检查项**:
|
||||
- XSS 攻击防护
|
||||
- CSRF Token 验证
|
||||
- 安全头部配置
|
||||
- 表单验证
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:security
|
||||
# 或
|
||||
npx playwright test --grep @security
|
||||
```
|
||||
|
||||
### 7. 视觉回归测试 (Visual Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/visual/`
|
||||
|
||||
**目的**: 检测 UI 变化
|
||||
|
||||
**测试文件**:
|
||||
- `home-page.visual.spec.ts` - 首页视觉测试
|
||||
- `contact-page.visual.spec.ts` - 联系页视觉测试
|
||||
- `visual-regression.spec.ts` - 视觉回归测试
|
||||
|
||||
**快照目录**:
|
||||
- `*-snapshots/` - 基线快照
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
npm run test:visual
|
||||
# 或
|
||||
npx playwright test --grep @visual
|
||||
```
|
||||
|
||||
**更新快照**:
|
||||
```bash
|
||||
npx playwright test --grep @visual --update-snapshots
|
||||
```
|
||||
|
||||
### 8. 移动端测试 (Mobile Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/mobile/`
|
||||
|
||||
**目的**: 验证移动端功能
|
||||
|
||||
**测试文件**:
|
||||
- `compatibility/mobile-compatibility.spec.ts` - 兼容性测试
|
||||
- `gesture/mobile-gesture.spec.ts` - 手势测试
|
||||
- `network/network-environment.spec.ts` - 网络环境测试
|
||||
- `performance/mobile-performance.spec.ts` - 移动性能测试
|
||||
- `pwa/pwa-functionality.spec.ts` - PWA 功能测试
|
||||
- `mobile-ux.spec.ts` - 移动端 UX 测试
|
||||
|
||||
### 9. 部署就绪测试 (Deployment Tests)
|
||||
|
||||
**目录**: `e2e/src/tests/deployment/`
|
||||
|
||||
**目的**: 验证部署前检查
|
||||
|
||||
**测试文件**:
|
||||
- `deployment-readiness.spec.ts` - 部署就绪检查
|
||||
- `quick-check.spec.ts` - 快速检查
|
||||
|
||||
## Page Object Model
|
||||
|
||||
### 基础页面类
|
||||
|
||||
```typescript
|
||||
// e2e/src/pages/BasePage.ts
|
||||
export class BasePage {
|
||||
readonly page: Page;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async navigate(path: string) {
|
||||
await this.page.goto(path);
|
||||
}
|
||||
|
||||
async waitForPageLoad() {
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async takeScreenshot(name: string) {
|
||||
await this.page.screenshot({ path: `screenshots/${name}.png` });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 首页 Page Object
|
||||
|
||||
```typescript
|
||||
// e2e/src/pages/HomePage.ts
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class HomePage extends BasePage {
|
||||
readonly heroSection: Locator;
|
||||
readonly servicesSection: Locator;
|
||||
readonly productsSection: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page);
|
||||
this.heroSection = page.locator('[data-testid="hero-section"]');
|
||||
this.servicesSection = page.locator('#services');
|
||||
this.productsSection = page.locator('#products');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.navigate('/');
|
||||
await this.waitForPageLoad();
|
||||
}
|
||||
|
||||
async scrollToSection(sectionId: string) {
|
||||
await this.page.locator(`#${sectionId}`).scrollIntoViewIfNeeded();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 使用示例
|
||||
|
||||
```typescript
|
||||
// e2e/src/tests/smoke/home-page.smoke.spec.ts
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { HomePage } from '../../pages/HomePage';
|
||||
|
||||
test.describe('首页冒烟测试', () => {
|
||||
let homePage: HomePage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
homePage = new HomePage(page);
|
||||
await homePage.goto();
|
||||
});
|
||||
|
||||
test('首页加载成功', async () => {
|
||||
await expect(homePage.heroSection).toBeVisible();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 测试配置
|
||||
|
||||
### Playwright 配置
|
||||
|
||||
```typescript
|
||||
// e2e/playwright.config.ts
|
||||
export default defineConfig({
|
||||
testDir: './src/tests',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 4 : '50%',
|
||||
reporter: [
|
||||
['html'],
|
||||
['json', { outputFile: 'test-results/results.json' }],
|
||||
['junit', { outputFile: 'test-results/junit.xml' }],
|
||||
['allure-playwright'],
|
||||
],
|
||||
timeout: 90000,
|
||||
use: {
|
||||
baseURL: 'http://localhost:3000',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'on-first-retry',
|
||||
headless: true,
|
||||
},
|
||||
projects: [
|
||||
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
||||
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
||||
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
||||
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
|
||||
{ name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },
|
||||
],
|
||||
webServer: {
|
||||
command: 'cd .. && npm run dev',
|
||||
url: 'http://localhost:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 环境配置
|
||||
|
||||
```typescript
|
||||
// e2e/src/config/environments.ts
|
||||
export interface Environment {
|
||||
name: string;
|
||||
baseURL: string;
|
||||
retries: number;
|
||||
trace: 'on' | 'off' | 'on-first-retry';
|
||||
screenshot: 'on' | 'off' | 'only-on-failure';
|
||||
video: 'on' | 'off' | 'on-first-retry';
|
||||
headless: boolean;
|
||||
slowMo: number;
|
||||
}
|
||||
|
||||
export const environments: Record<string, Environment> = {
|
||||
development: {
|
||||
name: 'development',
|
||||
baseURL: 'http://localhost:3000',
|
||||
retries: 0,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'on-first-retry',
|
||||
headless: true,
|
||||
slowMo: 0,
|
||||
},
|
||||
production: {
|
||||
name: 'production',
|
||||
baseURL: 'https://www.novalon.cn',
|
||||
retries: 2,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'on-first-retry',
|
||||
headless: true,
|
||||
slowMo: 0,
|
||||
},
|
||||
};
|
||||
|
||||
export function getEnvironment(): Environment {
|
||||
const env = process.env.TEST_ENV || 'development';
|
||||
return environments[env];
|
||||
}
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
|
||||
### 本地运行
|
||||
|
||||
```bash
|
||||
# 进入测试目录
|
||||
cd e2e
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# 安装浏览器
|
||||
npx playwright install
|
||||
|
||||
# 运行所有测试
|
||||
npm run test
|
||||
|
||||
# 运行特定测试
|
||||
npx playwright test path/to/test.spec.ts
|
||||
|
||||
# 运行带标签的测试
|
||||
npx playwright test --grep @smoke
|
||||
|
||||
# UI 模式
|
||||
npm run test:ui
|
||||
|
||||
# 调试模式
|
||||
npm run test:debug
|
||||
|
||||
# 有头模式
|
||||
npm run test:headed
|
||||
```
|
||||
|
||||
### CI 环境运行
|
||||
|
||||
```bash
|
||||
# CI 模式(禁止 only、增加重试)
|
||||
CI=true npx playwright test
|
||||
```
|
||||
|
||||
## 测试报告
|
||||
|
||||
### HTML 报告
|
||||
|
||||
```bash
|
||||
npx playwright show-report
|
||||
```
|
||||
|
||||
### Allure 报告
|
||||
|
||||
```bash
|
||||
# 生成报告
|
||||
npm run test:allure
|
||||
|
||||
# 打开报告
|
||||
npm run test:allure:open
|
||||
|
||||
# 实时服务
|
||||
npm run test:allure:serve
|
||||
```
|
||||
|
||||
### JUnit 报告
|
||||
|
||||
用于 CI 集成,输出到 `test-results/junit.xml`。
|
||||
|
||||
## 测试最佳实践
|
||||
|
||||
### 1. 使用数据测试 ID
|
||||
|
||||
```tsx
|
||||
// 组件中
|
||||
<div data-testid="hero-section">
|
||||
|
||||
// 测试中
|
||||
await page.locator('[data-testid="hero-section"]')
|
||||
```
|
||||
|
||||
### 2. 等待策略
|
||||
|
||||
```typescript
|
||||
// 等待元素可见
|
||||
await expect(locator).toBeVisible();
|
||||
|
||||
// 等待网络空闲
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 等待特定响应
|
||||
await page.waitForResponse('**/api/contact');
|
||||
```
|
||||
|
||||
### 3. 避免硬编码等待
|
||||
|
||||
```typescript
|
||||
// 不推荐
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// 推荐
|
||||
await expect(locator).toBeVisible();
|
||||
```
|
||||
|
||||
### 4. 使用 Fixtures
|
||||
|
||||
```typescript
|
||||
// e2e/src/fixtures/base.fixture.ts
|
||||
import { test as base } from '@playwright/test';
|
||||
import { HomePage } from '../pages/HomePage';
|
||||
|
||||
export const test = base.extend<{
|
||||
homePage: HomePage;
|
||||
}>({
|
||||
homePage: async ({ page }, use) => {
|
||||
const homePage = new HomePage(page);
|
||||
await use(homePage);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 5. 测试隔离
|
||||
|
||||
```typescript
|
||||
test.describe('测试组', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// 每个测试前的初始化
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
// 每个测试后的清理
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## CI 集成
|
||||
|
||||
### Woodpecker CI 配置
|
||||
|
||||
```yaml
|
||||
# .woodpecker.yml
|
||||
pipeline:
|
||||
e2e-tests:
|
||||
image: node:18-alpine
|
||||
environment:
|
||||
NODE_ENV: test
|
||||
CI: true
|
||||
commands:
|
||||
- cd e2e
|
||||
- npm ci
|
||||
- npx playwright install --with-deps chromium
|
||||
- npm run test:ci
|
||||
when:
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
```
|
||||
|
||||
### 测试命令
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test": "playwright test",
|
||||
"test:smoke": "playwright test --grep @smoke",
|
||||
"test:regression": "playwright test --grep @regression",
|
||||
"test:performance": "playwright test --grep @performance",
|
||||
"test:responsive": "playwright test --grep @responsive",
|
||||
"test:visual": "playwright test --grep @visual",
|
||||
"test:accessibility": "playwright test --grep @accessibility",
|
||||
"test:security": "playwright test --grep @security",
|
||||
"test:ci": "playwright test --reporter=html,json,junit"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### 1. Trace Viewer
|
||||
|
||||
```bash
|
||||
# 运行测试并生成 trace
|
||||
npx playwright test --trace on
|
||||
|
||||
# 查看 trace
|
||||
npx playwright show-trace trace.zip
|
||||
```
|
||||
|
||||
### 2. 截图和视频
|
||||
|
||||
```typescript
|
||||
// 手动截图
|
||||
await page.screenshot({ path: 'debug.png' });
|
||||
|
||||
// 元素截图
|
||||
await locator.screenshot({ path: 'element.png' });
|
||||
|
||||
// 全页截图
|
||||
await page.screenshot({ path: 'full.png', fullPage: true });
|
||||
```
|
||||
|
||||
### 3. 控制台日志
|
||||
|
||||
```typescript
|
||||
// 监听控制台
|
||||
page.on('console', msg => console.log(msg.text()));
|
||||
|
||||
// 监听页面错误
|
||||
page.on('pageerror', error => console.error(error));
|
||||
```
|
||||
|
||||
### 4. Playwright Inspector
|
||||
|
||||
```bash
|
||||
npx playwright test --debug
|
||||
```
|
||||
Reference in New Issue
Block a user