Files
E2E-Test/ARCHITECTURE.md
2025-11-04 16:29:07 +08:00

362 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 项目架构说明
## 设计理念
本项目采用 **Page Object Model (POM)** 设计模式,遵循以下原则:
1. **关注点分离**:页面逻辑与测试逻辑分离
2. **DRY 原则**:避免重复代码
3. **单一职责**:每个类只负责一个页面或功能
4. **可测试性**:易于编写和维护测试
5. **可扩展性**:便于添加新功能
## 架构图
```
┌─────────────────────────────────────────────────┐
│ 测试文件层 │
│ (文书管理-refactored.spec.ts) │
│ - 组织测试流程 │
│ - 使用 Page Objects │
│ - 错误处理 │
└─────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ Page Object 层 │
│ - LoginPage (登录) │
│ - StudentPage (学生管理) │
│ - DreamiExplorePage (AI聊天) │
│ - EssayWritingPage (文书写作) │
│ - BasePage (基础页面类) │
└─────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 工具层 │
│ - ErrorHandler (错误处理) │
│ - 其他工具类... │
└─────────────────┬───────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ 配置层 │
│ - TestConfig (测试配置) │
│ - 环境变量、超时设置、测试数据 │
└─────────────────────────────────────────────────┘
```
## 核心组件
### 1. 配置层 (config/)
**职责**:集中管理所有配置信息
#### test.config.ts
```typescript
export const TestConfig = {
env: { ... }, // 环境配置
credentials: { ... }, // 登录凭证
timeouts: { ... }, // 超时设置
testData: { ... }, // 测试数据
waits: { ... }, // 等待时间
report: { ... }, // 报告配置
}
```
**优势**
- ✅ 环境切换方便
- ✅ 配置集中管理
- ✅ 避免硬编码
- ✅ 易于维护
### 2. Page Object 层 (pages/)
**职责**:封装页面操作,提供高级 API
#### BasePage.ts
基础页面类,所有页面对象的父类
```typescript
export class BasePage {
protected page: Page;
protected config = TestConfig;
// 通用方法
async waitForPageStable() { }
async goto(url: string) { }
async click(selector: string) { }
// ...
}
```
#### LoginPage.ts
登录页面对象
```typescript
export class LoginPage extends BasePage {
async visitHomePage() { }
async clickLoginButton() { }
async login(email, password) { }
async performLogin() { }
}
```
**设计特点**
- 继承 BasePage 获取通用功能
- 方法名语义化,易于理解
- 封装页面元素定位
- 提供高级业务方法
#### StudentPage.ts
学生管理页面对象
```typescript
export class StudentPage extends BasePage {
async goToStudentWorkbench() { }
async createNewStudent(studentData) { }
}
```
#### EssayWritingPage.ts
文书写作页面对象(最复杂)
```typescript
export class EssayWritingPage extends BasePage {
// 基础操作
async enterEssayWriting() { }
async addMaterial(title, content, expectedText) { }
// AI 生成功能
async generateEssayIdea() { }
async generateEssay() { }
async generateOutline() { }
async generateDraft(level, length) { }
// 检查功能
async performGrammarCheck() { }
async performPlagiarismCheck() { }
async performAIDetection() { }
async performHumanize() { }
async performPolish() { }
async performGetRated() { }
async performImprovementCheck() { }
}
```
**优势**
- ✅ 业务逻辑清晰
- ✅ 方法可复用
- ✅ UI 变化只需修改 Page Object
- ✅ 测试文件更简洁
### 3. 工具层 (utils/)
**职责**:提供通用工具和辅助功能
#### ErrorHandler.ts
错误处理工具类
```typescript
export class ErrorHandler {
private errors: ErrorRecord[] = [];
// 记录错误
recordError(stepName, error, page) { }
// 执行步骤并捕获错误
async executeStep(stepName, stepFunction, page) { }
// 生成错误报告
generateErrorReport() { }
// 打印摘要
printSummary() { }
}
```
**功能**
- ✅ 自动捕获并记录错误
- ✅ 清理错误信息,去除技术细节
- ✅ 生成友好的错误报告
- ✅ 允许测试继续执行
### 4. 测试文件层
**职责**:组织测试流程,调用 Page Objects
#### 文书管理-refactored.spec.ts
```typescript
test('文书管理完整流程测试', async ({ page }) => {
// 初始化
const errorHandler = new ErrorHandler(TestConfig.credentials);
const loginPage = new LoginPage(page);
const essayWritingPage = new EssayWritingPage(page);
// 测试流程
await executeStep('访问首页', async () => {
await loginPage.visitHomePage();
});
await executeStep('生成 Essay', async () => {
await essayWritingPage.generateEssay();
});
// 生成报告
errorHandler.printSummary();
});
```
**特点**
- ✅ 代码清晰易读
- ✅ 业务流程一目了然
- ✅ 错误处理自动化
- ✅ 配置与代码分离
## 数据流
```
测试开始
读取 TestConfig
初始化 Page Objects
执行测试步骤 (executeStep)
调用 Page Object 方法
Page Object 执行页面操作
发生错误?
├─ 是 → ErrorHandler 记录错误 → 继续下一步
└─ 否 → 继续下一步
所有步骤完成
生成错误报告
测试结束
```
## 扩展指南
### 添加新页面
1. **创建 Page Object**
```typescript
// e2e/pages/NewPage.ts
import { Page, expect } from '@playwright/test';
import { BasePage } from './BasePage';
export class NewPage extends BasePage {
constructor(page: Page) {
super(page);
}
async yourMethod(): Promise<void> {
// 实现页面操作
}
}
```
2. **在测试中使用**
```typescript
import { NewPage } from './pages/NewPage';
test('your test', async ({ page }) => {
const newPage = new NewPage(page);
await newPage.yourMethod();
});
```
### 添加新配置
`test.config.ts` 中添加:
```typescript
export const TestConfig = {
// ... 现有配置
newConfig: {
// 新配置项
}
}
```
### 添加新工具类
```typescript
// e2e/utils/YourUtil.ts
export class YourUtil {
// 实现工具方法
}
```
## 最佳实践
### 1. Page Object 设计
- ✅ 一个页面一个类
- ✅ 方法返回 Promise<void>
- ✅ 使用描述性方法名
- ✅ 避免在 Page Object 中写断言(除非是验证页面状态)
### 2. 测试文件编写
- ✅ 使用 executeStep 包装每个步骤
- ✅ 步骤名称清晰描述操作
- ✅ 复杂流程拆分为多个步骤
- ✅ 在测试末尾生成报告
### 3. 配置管理
- ✅ 不要硬编码任何配置值
- ✅ 环境相关的配置放在 env 中
- ✅ 测试数据放在 testData 中
- ✅ 超时设置放在 timeouts 中
### 4. 错误处理
- ✅ 所有关键步骤都用 executeStep 包装
- ✅ 不要在 Page Object 中处理错误
- ✅ 让错误冒泡到测试层处理
- ✅ 记录足够的错误上下文信息
## 代码复用
### 前后对比
**重构前**
```typescript
// 代码重复,难以维护
await page.getByRole('button', { name: 'Add Material' }).click();
await page.getByRole('menuitem', { name: 'Manual Add' }).click();
await page.getByRole('textbox', { name: 'Title' }).fill('title');
// ... 10+ 行相似代码
```
**重构后**
```typescript
// 简洁清晰,易于维护
await essayWritingPage.addMaterial(
TestConfig.testData.material.title,
TestConfig.testData.material.content,
TestConfig.testData.material.expectedButtonText
);
```
## 性能优化
- Page Object 方法中使用合理的超时
- 配置中集中管理超时时间
- 避免不必要的等待
- 使用页面对象缓存(如需要)
## 总结
这个架构具有以下优势:
1. **可维护性**:模块化设计,职责清晰
2. **可扩展性**:易于添加新功能
3. **可读性**:代码清晰,易于理解
4. **可测试性**:每个组件独立可测
5. **健壮性**:完善的错误处理机制
通过这种架构,测试代码变得像文档一样易读,维护成本大大降低。