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

E2E测试框架使用文档

目录

概述

本E2E测试框架基于Playwright构建,为uniapp和admin模块提供完整的端到端测试解决方案。框架采用模块化设计,支持前后端完全联通的测试场景,确保业务流程的完整性和正确性。

主要特性

  • 基于Playwright的现代化E2E测试框架
  • 支持uniapp和admin模块的全流程业务测试
  • 完整的测试数据管理和清理机制
  • 丰富的测试辅助工具(表单、表格、断言等)
  • 详细的测试日志和截图功能
  • 多种测试报告格式(HTML、JSON、JUnit
  • 完整的CI/CD集成支持
  • 支持多浏览器和多设备测试

快速开始

环境要求

  • Node.js >= 18.0.0
  • npm >= 9.0.0
  • Docker(用于运行测试环境)

安装依赖

cd everything-is-suitable-test
npm install

安装Playwright浏览器

npm run test:install

配置环境变量

复制环境变量示例文件:

cp .env.example .env

编辑.env文件,配置测试环境:

TEST_ENV=local
ADMIN_BASE_URL=http://localhost:5174
UNIAPP_BASE_URL=http://localhost:8081
API_BASE_URL=http://127.0.0.1:8080
TEST_USERNAME=admin
TEST_PASSWORD=admin123
MOCK_ENABLED=false
TEST_TIMEOUT=30000

运行测试

npm run test

项目结构

everything-is-suitable-test/
├── e2e/
│   ├── fixtures/              # 测试夹具
│   │   └── test-fixtures.ts  # 自定义测试夹具
│   ├── core/                 # 核心模块
│   │   ├── test-config.ts     # 测试配置管理
│   │   ├── test-logger.ts     # 测试日志记录
│   │   ├── test-reporter.ts   # 测试报告生成
│   │   └── test-data-manager.ts # 测试数据管理
│   ├── helpers/              # 测试辅助工具
│   │   ├── api-helper.ts     # API请求辅助
│   │   ├── form-helper.ts    # 表单操作辅助
│   │   ├── table-helper.ts   # 表格操作辅助
│   │   ├── screenshot-helper.ts # 截图辅助
│   │   └── assertion-helper.ts  # 断言辅助
│   └── examples/            # 示例测试
│       ├── user-management.spec.ts
│       ├── api-integration.spec.ts
│       ├── uniapp-almanac.spec.ts
│       ├── uniapp-user.spec.ts
│       ├── admin-user-management.spec.ts
│       └── admin-role-management.spec.ts
├── playwright.config.ts       # Playwright配置
├── package.json
├── tsconfig.json
└── .env.example

核心功能

测试夹具 (Fixtures)

框架提供了一组自定义测试夹具,可以在测试中直接使用:

test('示例测试', async ({ 
  page, 
  testConfig, 
  testLogger, 
  testDataManager, 
  apiHelper, 
  formHelper, 
  tableHelper, 
  screenshotHelper, 
  assertionHelper 
}) => {
  // 使用夹具进行测试
});

可用夹具

夹具名称 描述
testConfig 测试配置管理
testLogger 测试日志记录
testReporter 测试报告生成
testDataManager 测试数据管理
apiHelper API请求辅助
formHelper 表单操作辅助
tableHelper 表格操作辅助
screenshotHelper 截图辅助
assertionHelper 断言辅助

测试数据管理

TestDataManager提供完整的测试数据生命周期管理:

const testUser = await testDataManager.createTestUser({
  realName: '测试用户',
  email: 'test@example.com'
});

// 测试完成后自动清理

测试辅助工具

APIHelper

await apiHelper.login('admin', 'admin123');
const response = await apiHelper.get('/api/sys/user');
await apiHelper.post('/api/sys/user', userData);
await apiHelper.put('/api/sys/user', updateData);
await apiHelper.delete(`/api/sys/user/${userId}`);

FormHelper

await formHelper.fillField('input[name="username"]', 'testuser');
await formHelper.fillForm({
  'input[name="username"]': { value: 'testuser' },
  'input[name="password"]': { value: 'password123' }
});
await formHelper.submitForm('button[type="submit"]');

TableHelper

const rowCount = await tableHelper.getRowCount('.user-table');
const cellText = await tableHelper.getCellText('.user-table', 1, 2);
const matchingRows = await tableHelper.findRowsByCellText('.user-table', 'testuser');
await tableHelper.clickRow('.user-table', 1);

AssertionHelper

await assertionHelper.assertElementVisible(page, '.user-table');
await assertionHelper.assertElementText(page, '.user-name', '张三');
await assertionHelper.assertSuccessMessage(page);
await assertionHelper.assertAPISuccess(response);

ScreenshotHelper

await screenshotHelper.takeScreenshot('test-name');
await screenshotHelper.takeElementScreenshot('.element', 'element-name');
await screenshotHelper.takeScreenshotOnFailure('test-name');
await screenshotHelper.takeScreenshotOnSuccess('test-name');

编写测试

基本测试结构

import { test, expect } from './fixtures/test-fixtures';

test.describe('功能模块测试', () => {
  test.beforeEach(async ({ page, testConfig, testLogger }) => {
    testLogger.startTest('功能模块测试');
    await page.goto(testConfig.getEnvironment().baseURL);
  });

  test.afterEach(async ({ testLogger }) => {
    testLogger.endTest('功能模块测试', 'passed');
  });

  test('TC-001: 测试用例名称', async ({ 
    page, 
    formHelper, 
    assertionHelper,
    testLogger,
    screenshotHelper
  }) => {
    testLogger.startStep('步骤1: 描述步骤');
    
    // 测试逻辑
    
    testLogger.endStep('步骤1: 描述步骤', 'passed');
    
    await screenshotHelper.takeScreenshotOnSuccess('test-name');
  });
});

测试命名规范

  • 测试套件:使用test.describe()组织相关测试
  • 测试用例:使用TC-模块-序号: 描述格式
  • 测试步骤:使用步骤N: 描述格式

测试最佳实践

  1. 使用测试夹具:充分利用框架提供的夹具,避免重复代码
  2. 详细日志:使用testLogger记录测试步骤,便于调试
  3. 截图记录:在关键步骤和测试结束时截图
  4. 数据清理:使用testDataManager管理测试数据,自动清理
  5. 断言明确:使用assertionHelper进行清晰的断言
  6. 等待稳定:使用waitFor等待元素状态稳定

运行测试

本地运行

# 运行所有测试
npm run test

# 运行特定测试文件
npx playwright test e2e/examples/user-management.spec.ts

# 运行特定测试用例
npx playwright test -g "TC-USER-001"

# 有头模式运行
npm run test:headed

# 调试模式
npm run test:debug

# UI模式
npm run test:ui

Docker环境运行

# 启动测试环境
docker-compose -f docker-compose.test.yml up -d

# 等待服务启动
sleep 30

# 运行测试
npm run test

# 停止环境
docker-compose -f docker-compose.test.yml down

查看测试报告

# HTML报告
npm run test:report

# JSON报告
cat test-results/results.json

# JUnit报告
cat test-results/junit.xml

CI/CD集成

Woodpecker CI

测试框架已集成Woodpecker CI,配置文件位于项目根目录的.woodpecker.yml

触发条件

  • 推送到main或develop分支
  • 创建Pull Request
  • 每天凌晨2点定时执行

测试流程

  1. 启动Docker测试环境
  2. 并行运行E2E测试(4个分片)
  3. 执行API集成测试
  4. 生成HTML测试报告
  5. 测试失败时发送Slack通知

Pipeline结构

  • setup: 初始化Docker环境
  • e2e-tests: 执行端到端测试(并行4个分片)
  • api-tests: 执行API集成测试
  • test-report: 合并测试报告
  • notify-failure: 测试失败时发送通知
  • nightly-tests: 每日定时执行完整测试

详细配置说明请参考:WOODPECKER_CI.md

测试报告

测试完成后会生成以下报告:

  1. HTML报告:交互式HTML报告,包含测试详情和截图
  2. JSON报告:机器可读的JSON格式报告
  3. JUnit报告:兼容JUnit的XML格式报告

报告位置:

  • playwright-report/HTML报告
  • test-results/results.jsonJSON报告
  • test-results/junit.xmlJUnit报告

最佳实践

1. 测试设计原则

  • 测试金字塔:70% API测试,20% 集成测试,10% E2E测试
  • 独立性:每个测试用例应该独立运行,不依赖其他测试
  • 可重复性:测试结果应该稳定可重复
  • 快速反馈:优先测试核心业务流程

2. 数据管理

  • 使用TestDataManager创建和管理测试数据
  • 测试完成后自动清理测试数据
  • 避免硬编码测试数据,使用工厂模式生成

3. 错误处理

  • 使用try-catch捕获预期异常
  • 提供清晰的错误信息
  • 失败时自动截图和记录日志

4. 性能优化

  • 合理使用并行执行
  • 避免不必要的等待
  • 复用浏览器实例

5. 维护性

  • 遵循代码规范
  • 添加必要的注释
  • 定期重构测试代码
  • 更新测试文档

故障排查

常见问题

1. 测试超时

问题:测试执行超时

解决方案

// 增加超时时间
test.setTimeout(60000);

// 或在配置中设置
use: {
  actionTimeout: 60000,
  navigationTimeout: 60000
}

2. 元素未找到

问题:无法定位页面元素

解决方案

// 等待元素可见
await page.waitForSelector('.element', { timeout: 10000 });

// 使用更精确的选择器
await page.locator('.container .element').click();

3. 测试数据冲突

问题:测试数据相互干扰

解决方案

// 使用唯一标识符
const timestamp = Date.now();
const username = `test_user_${timestamp}`;

// 每个测试使用独立数据
beforeEach(async () => {
  testUser = await testDataManager.createTestUser();
});

afterEach(async () => {
  await testDataManager.cleanup();
});

4. 环境配置问题

问题:无法连接到测试环境

解决方案

# 检查环境变量
cat .env

# 验证服务状态
docker-compose ps

# 查看服务日志
docker-compose logs backend

调试技巧

1. 使用调试模式

npm run test:debug

2. 使用UI模式

npm run test:ui

3. 查看详细日志

testLogger.debug('调试信息');
testLogger.info('一般信息');
testLogger.warn('警告信息');
testLogger.error('错误信息', error);

4. 截图和录屏

// 失败时自动截图
await screenshotHelper.takeScreenshotOnFailure('test-name');

// 手动截图
await screenshotHelper.takeScreenshot('debug-point');

// 录屏(在playwright.config.ts中配置)
use: {
  video: 'retain-on-failure'
}

性能优化

1. 减少测试时间

// 并行执行测试
workers: 4

// 跳过慢速测试
test.skip('慢速测试', async () => {});

// 使用项目分组
projects: [
  { name: 'fast', testMatch: '**/*.fast.spec.ts' },
  { name: 'slow', testMatch: '**/*.slow.spec.ts' }
]

2. 优化等待时间

// 使用智能等待
await page.waitForSelector('.element', { state: 'visible' });

// 避免固定等待
// 不推荐:await page.waitForTimeout(5000);
// 推荐:await page.waitForLoadState('networkidle');

贡献指南

添加新测试

  1. e2e/examples/目录下创建新的测试文件
  2. 遵循测试命名规范
  3. 使用测试夹具和辅助工具
  4. 添加详细的测试步骤和日志
  5. 提交前运行测试确保通过

扩展框架

  1. e2e/helpers/目录下添加新的辅助工具
  2. e2e/fixtures/test-fixtures.ts中注册新的夹具
  3. 更新文档说明新功能
  4. 添加示例测试

许可证

MIT License

联系方式

如有问题或建议,请联系测试团队。