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

9.3 KiB
Raw Blame History

项目架构说明

设计理念

本项目采用 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

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 记录错误 → 继续下一步
   └─ 否 → 继续下一步
   ↓
所有步骤完成
   ↓
生成错误报告
   ↓
测试结束

扩展指南

添加新页面

  1. 创建 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> {
    // 实现页面操作
  }
}
  1. 在测试中使用
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 方法中使用合理的超时
  • 配置中集中管理超时时间
  • 避免不必要的等待
  • 使用页面对象缓存(如需要)

总结

这个架构具有以下优势:

  1. 可维护性:模块化设计,职责清晰
  2. 可扩展性:易于添加新功能
  3. 可读性:代码清晰,易于理解
  4. 可测试性:每个组件独立可测
  5. 健壮性:完善的错误处理机制

通过这种架构,测试代码变得像文档一样易读,维护成本大大降低。