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