# E2E测试工具使用指南 ## 概述 本E2E测试工具是一个基于Playwright的可复用端到端测试框架,提供了模块化的测试用例编写能力、统一的测试环境配置、常用测试操作的封装与复用,以及清晰的测试报告与日志输出能力。 ## 目录结构 ``` e2e/ ├── core/ # 核心模块 │ ├── test-config.ts # 测试配置管理 │ ├── test-data.ts # 测试数据生成器 │ ├── test-logger.ts # 测试日志记录器 │ └── test-reporter.ts # 测试报告生成器 ├── pages/ # 页面对象模型 │ ├── base-page.ts # 基础页面类 │ ├── login-page.ts # 登录页面 │ ├── dashboard-page.ts # 仪表盘页面 │ ├── user-management-page.ts # 用户管理页面 │ ├── role-management-page.ts # 角色管理页面 │ └── menu-management-page.ts # 菜单管理页面 ├── helpers/ # 测试辅助工具 │ ├── screenshot-helper.ts # 截图辅助工具 │ ├── form-helper.ts # 表单辅助工具 │ └── table-helper.ts # 表格辅助工具 ├── fixtures/ # 测试夹具 ├── utils/ # 工具函数 │ └── common-utils.ts # 通用工具函数 ├── constants/ # 常量定义 │ └── index.ts # 常量集合 ├── examples/ # 示例测试 │ └── complete-example.spec.ts ├── test-fixtures.ts # Playwright测试夹具 └── mock-manager.ts # Mock服务管理器 ``` ## 快速开始 ### 1. 安装依赖 ```bash npm install --save-dev @playwright/test ``` ### 2. 配置测试环境 在项目根目录创建 `.env.e2e` 文件: ```env # E2E测试环境配置 E2E_ENV=local E2E_BASE_URL=http://localhost:5173 E2E_MOCK_ENABLED=true E2E_MOCK_MODE=full ``` ### 3. 编写测试用例 使用提供的测试夹具编写测试用例: ```typescript import { test, expect } from './test-fixtures'; test.describe('登录功能测试', () => { test('成功登录', async ({ pageObjects, testData, testLogger }) => { testLogger.startTest('成功登录'); try { await pageObjects.loginPage.navigate(); await pageObjects.loginPage.login(testData.admin.username, testData.admin.password); await pageObjects.dashboardPage.waitForLoad(); const pageTitle = await pageObjects.dashboardPage.getPageTitle(); expect(pageTitle).toContain('仪表盘'); testLogger.endTest('成功登录', 'passed'); } catch (error) { testLogger.endTest('成功登录', 'failed', error as Error); throw error; } }); }); ``` ### 4. 运行测试 ```bash # 运行所有测试 npm run test:e2e # 运行特定测试文件 npx playwright test e2e/examples/complete-example.spec.ts # 运行特定测试用例 npx playwright test -g "成功登录" # 调试模式运行 npx playwright test --debug ``` ## 核心功能 ### 1. 测试配置管理 `test-config.ts` 提供了统一的测试环境配置管理: ```typescript import { testConfig } from './core/test-config'; // 获取当前环境配置 const env = testConfig.getEnvironment(); console.log(env.name); // 环境名称 console.log(env.baseURL); // 基础URL console.log(env.mockEnabled); // Mock是否启用 console.log(env.timeout); // 超时配置 // 切换环境 testConfig.setEnvironment('dev'); // 获取特定环境配置 const devConfig = testConfig.getEnvironment('dev'); ``` ### 2. 测试数据生成 `test-data.ts` 提供了测试数据生成器: ```typescript import { testDataGenerator } from './core/test-data'; // 生成用户数据 const userData = testDataGenerator.generateUserData({ username: 'testuser', email: 'test@example.com', status: 'active' }); // 生成角色数据 const roleData = testDataGenerator.generateRoleData({ roleName: '测试角色', roleCode: 'test_role', status: 1 }); // 生成菜单数据 const menuData = testDataGenerator.generateMenuData({ menuName: '测试菜单', menuType: 1, path: '/test', status: 0 }); // 生成权限数据 const permissionData = testDataGenerator.generatePermissionData({ permissionName: '测试权限', permissionCode: 'test:permission', permissionType: 'button' }); ``` ### 3. 测试日志记录 `test-logger.ts` 提供了结构化的测试日志记录: ```typescript import { testLogger } from './core/test-logger'; // 开始测试 testLogger.startTest('测试名称'); // 开始测试步骤 testLogger.startStep('步骤名称'); // 记录不同级别的日志 testLogger.debug('调试信息'); testLogger.info('普通信息'); testLogger.warn('警告信息'); testLogger.error('错误信息', error); // 结束测试步骤 testLogger.endStep('步骤名称', 'passed'); // 结束测试 testLogger.endTest('测试名称', 'passed'); ``` ### 4. 测试报告生成 `test-reporter.ts` 提供了测试报告生成功能: ```typescript import { testReporter } from './core/test-reporter'; // 开始测试报告 testReporter.startReport(); // 记录测试结果 testReporter.recordTestResult({ testName: '测试名称', status: 'passed', duration: 1000, steps: [], logs: [], screenshots: [], errors: [] }); // 生成所有报告 await testReporter.generateAllReports('./test-results/reports'); // 生成JSON报告 await testReporter.generateJSONReport('./test-results/reports/e2e-report.json'); // 生成HTML报告 await testReporter.generateHTMLReport('./test-results/reports/e2e-report.html'); ``` ### 5. 页面对象模型 所有页面类都继承自 `BasePage`,提供统一的页面操作接口: ```typescript import { BasePage } from './pages/base-page'; import { LoginPage } from './pages/login-page'; import { DashboardPage } from './pages/dashboard-page'; // 使用页面对象 const loginPage = new LoginPage(page); await loginPage.navigate(); await loginPage.login('admin', 'password'); const dashboardPage = new DashboardPage(page); await dashboardPage.waitForLoad(); const title = await dashboardPage.getPageTitle(); ``` ### 6. 测试辅助工具 #### 截图辅助工具 ```typescript import { ScreenshotHelper } from './helpers/screenshot-helper'; const screenshotHelper = new ScreenshotHelper(page); // 截取当前页面 await screenshotHelper.takeScreenshot('page-screenshot'); // 截取整个页面 await screenshotHelper.takeFullPageScreenshot('full-page'); // 截取特定元素 await screenshotHelper.takeElementScreenshot('element-screenshot', '.selector'); // 在测试失败时自动截图 await screenshotHelper.takeScreenshotOnFailure('test-failure'); ``` #### 表单辅助工具 ```typescript import { FormHelper } from './helpers/form-helper'; const formHelper = new FormHelper(page); // 填写表单字段 await formHelper.fillField('input[name="username"]', 'testuser'); await formHelper.fillField('input[type="password"]', 'password', 'password'); await formHelper.fillField('select[name="role"]', 'admin', 'select'); await formHelper.fillField('input[type="checkbox"]', true, 'checkbox'); // 填写整个表单 await formHelper.fillForm({ username: 'testuser', password: 'password', email: 'test@example.com', role: 'admin' }); // 提交表单 await formHelper.submitForm(); // 重置表单 await formHelper.resetForm(); // 验证表单 const isValid = await formHelper.validateForm(); ``` #### 表格辅助工具 ```typescript import { TableHelper } from './helpers/table-helper'; const tableHelper = new TableHelper(page); // 获取表格行数 const rowCount = await tableHelper.getRowCount('.ant-table'); // 获取表格列数 const columnCount = await tableHelper.getColumnCount('.ant-table'); // 获取单元格文本 const cellText = await tableHelper.getCellText('.ant-table', 0, 0); // 获取整行数据 const rowData = await tableHelper.getRowData('.ant-table', 0); // 获取整列数据 const columnData = await tableHelper.getColumnData('.ant-table', 0); // 点击表格行 await tableHelper.clickRow('.ant-table', 0); // 点击表格单元格 await tableHelper.clickCell('.ant-table', 0, 0); // 等待表格加载 await tableHelper.waitForTableLoad('.ant-table'); // 验证表格数据 const isValid = await tableHelper.validateTableData('.ant-table', expectedData); ``` ### 7. Mock服务集成 ```typescript import { MockManager } from './mock-manager'; const mockConfig = { enabled: true, mode: 'full', mockPaths: [], delay: 0, logCalls: true, validateResponses: true, dataSource: 'memory' }; const mockManager = new MockManager(mockConfig); // 拦截API请求 await mockManager.interceptAPIRequest(page); // 添加Mock响应 mockManager.addMockResponse({ url: '/api/login', method: 'POST', response: { code: 200, data: { token: 'mock-token', userInfo: { id: 1, username: 'admin' } } } }); // 清除Mock响应 mockManager.clearMockResponses(); ``` ## 测试夹具 本工具提供了以下测试夹具: ### pageObjects 提供所有页面对象的实例: ```typescript test('使用页面对象', async ({ pageObjects }) => { await pageObjects.loginPage.navigate(); await pageObjects.loginPage.login('admin', 'password'); await pageObjects.dashboardPage.waitForLoad(); }); ``` ### helpers 提供所有辅助工具的实例: ```typescript test('使用辅助工具', async ({ helpers }) => { await helpers.screenshot.takeScreenshot('test'); await helpers.form.fillField('input[name="username"]', 'test'); const rowCount = await helpers.table.getRowCount('.ant-table'); }); ``` ### testData 提供预定义的测试数据: ```typescript test('使用测试数据', async ({ testData }) => { console.log(testData.user); // 普通用户数据 console.log(testData.admin); // 管理员数据 console.log(testData.role); // 角色数据 console.log(testData.menu); // 菜单数据 console.log(testData.permission); // 权限数据 }); ``` ### mockManager 提供Mock服务管理器: ```typescript test('使用Mock服务', async ({ mockManager }) => { mockManager.addMockResponse({ url: '/api/test', method: 'GET', response: { code: 200, data: 'mock data' } }); }); ``` ### testConfig 提供测试配置: ```typescript test('使用测试配置', async ({ testConfig }) => { console.log(testConfig.name); // 环境名称 console.log(testConfig.baseURL); // 基础URL console.log(testConfig.mockEnabled); // Mock是否启用 }); ``` ### testLogger 提供测试日志记录器: ```typescript test('使用测试日志', async ({ testLogger }) => { testLogger.info('测试信息'); testLogger.error('测试错误', error); }); ``` ### testReporter 提供测试报告生成器: ```typescript test('使用测试报告', async ({ testReporter }) => { testReporter.recordTestResult({ testName: '测试名称', status: 'passed', duration: 1000 }); }); ``` ## 最佳实践 ### 1. 测试用例组织 - 使用 `test.describe` 组织相关的测试用例 - 使用 `test.beforeEach` 和 `test.afterEach` 设置测试前置和后置条件 - 为每个测试用例提供清晰的描述 ```typescript test.describe('用户管理功能', () => { test.beforeEach(async ({ pageObjects, testData }) => { await pageObjects.loginPage.navigate(); await pageObjects.loginPage.login(testData.admin.username, testData.admin.password); }); test.afterEach(async ({ helpers }) => { await helpers.screenshot.takeScreenshot('after-test'); }); test('创建用户', async ({ pageObjects }) => { // 测试逻辑 }); }); ``` ### 2. 页面对象使用 - 始终使用页面对象而不是直接操作页面元素 - 将页面选择器封装在页面对象中 - 在页面对象中实现业务逻辑方法 ```typescript // 好的做法 await pageObjects.loginPage.login('admin', 'password'); // 不好的做法 await page.fill('input[placeholder="请输入用户名"]', 'admin'); await page.fill('input[placeholder="请输入密码"]', 'password'); await page.click('button[type="submit"]'); ``` ### 3. 测试数据管理 - 使用测试数据生成器创建测试数据 - 避免硬编码测试数据 - 使用测试夹具提供的预定义数据 ```typescript // 好的做法 const userData = testDataGenerator.generateUserData({ username: 'testuser', status: 'active' }); // 不好的做法 const userData = { username: 'testuser', password: 'password', email: 'test@example.com', // ... 硬编码的数据 }; ``` ### 4. 错误处理 - 在测试用例中使用 try-catch 捕获错误 - 使用测试日志记录错误信息 - 在测试失败时截图 ```typescript test('测试用例', async ({ testLogger, helpers }) => { testLogger.startTest('测试用例'); try { // 测试逻辑 testLogger.endTest('测试用例', 'passed'); } catch (error) { testLogger.endTest('测试用例', 'failed', error as Error); await helpers.screenshot.takeScreenshot('test-failure'); throw error; } }); ``` ### 5. 等待策略 - 使用页面对象提供的等待方法 - 避免使用固定的等待时间 - 使用 Playwright 的自动等待机制 ```typescript // 好的做法 await pageObjects.dashboardPage.waitForLoad(); await page.waitForSelector('.element', { state: 'visible' }); // 不好的做法 await page.waitForTimeout(5000); ``` ### 6. 断言使用 - 使用 Playwright 的 expect 断言 - 提供有意义的断言消息 - 验证关键的业务逻辑 ```typescript // 好的做法 expect(pageTitle).toContain('仪表盘'); expect(successMessage).toBeTruthy(); expect(rowCount).toBeGreaterThan(0); // 不好的做法 expect(pageTitle).toBeTruthy(); ``` ## 故障排查 ### 测试失败时的调试 1. 查看测试日志:`test-results/logs/` 2. 查看截图:`test-results/screenshots/` 3. 查看测试报告:`test-results/reports/` 4. 使用调试模式运行:`npx playwright test --debug` ### 常见问题 1. **元素未找到** - 检查选择器是否正确 - 确保元素已加载 - 使用适当的等待策略 2. **测试超时** - 增加超时配置 - 检查网络请求是否正常 - 优化测试等待策略 3. **Mock服务不工作** - 确认Mock服务已启用 - 检查Mock配置是否正确 - 验证Mock响应格式 ## 扩展开发 ### 添加新的页面对象 1. 在 `pages/` 目录下创建新的页面类 2. 继承 `BasePage` 类 3. 实现页面特定的方法和选择器 ```typescript import { BasePage } from './base-page'; export class NewPage extends BasePage { private readonly selectors = { // 页面选择器 }; constructor(page: Page) { super(page); } // 页面方法 } ``` ### 添加新的辅助工具 1. 在 `helpers/` 目录下创建新的辅助工具类 2. 实现辅助工具方法 3. 在测试夹具中注册新的辅助工具 ```typescript export class NewHelper { private page: Page; constructor(page: Page) { this.page = page; } // 辅助工具方法 } ``` ### 添加新的测试数据生成器 1. 在 `test-data.ts` 中添加新的数据生成方法 2. 定义数据接口 3. 实现数据生成逻辑 ```typescript export interface NewData { // 数据接口 } generateNewData(overrides: Partial = {}): NewData { // 数据生成逻辑 } ``` ## 性能优化 ### 并行执行 Playwright 默认支持并行执行测试,可以通过配置文件调整: ```typescript export default defineConfig({ workers: 4, // 并发工作进程数 fullyParallel: true, // 完全并行执行 }); ``` ### 测试隔离 确保每个测试用例都是独立的,避免测试之间的依赖: ```typescript test.beforeEach(async ({ page }) => { // 清理测试数据 // 重置测试状态 }); ``` ### 重试机制 配置测试失败时的重试次数: ```typescript export default defineConfig({ retries: 2, // 失败时重试次数 }); ``` ## 持续集成 ### CI/CD集成 在CI/CD流程中集成E2E测试: ```yaml # .github/workflows/e2e-tests.yml name: E2E Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - run: npm ci - run: npm run test:e2e - uses: actions/upload-artifact@v2 if: failure() with: name: test-results path: test-results/ ``` ## 总结 本E2E测试工具提供了完整的端到端测试解决方案,包括: - ✅ 模块化的测试用例编写 - ✅ 统一的测试环境配置 - ✅ 常用测试操作的封装与复用 - ✅ 清晰的测试报告与日志输出 - ✅ 页面对象模型(POM) - ✅ 测试辅助工具 - ✅ Mock服务集成 - ✅ 测试数据生成 通过使用本工具,可以高效地编写、执行和维护E2E测试,确保应用的质量和稳定性。