Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

734 lines
17 KiB
Markdown
Raw Permalink 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.
# 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> = {}): 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测试,确保应用的质量和稳定性。