first commit
This commit is contained in:
361
ARCHITECTURE.md
Normal file
361
ARCHITECTURE.md
Normal file
@ -0,0 +1,361 @@
|
||||
# 项目架构说明
|
||||
|
||||
## 设计理念
|
||||
|
||||
本项目采用 **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. **健壮性**:完善的错误处理机制
|
||||
|
||||
通过这种架构,测试代码变得像文档一样易读,维护成本大大降低。
|
||||
Reference in New Issue
Block a user