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

17 KiB
Raw Blame History

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. 安装依赖

npm install --save-dev @playwright/test

2. 配置测试环境

在项目根目录创建 .env.e2e 文件:

# E2E测试环境配置
E2E_ENV=local
E2E_BASE_URL=http://localhost:5173
E2E_MOCK_ENABLED=true
E2E_MOCK_MODE=full

3. 编写测试用例

使用提供的测试夹具编写测试用例:

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. 运行测试

# 运行所有测试
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 提供了统一的测试环境配置管理:

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 提供了测试数据生成器:

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 提供了结构化的测试日志记录:

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 提供了测试报告生成功能:

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,提供统一的页面操作接口:

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. 测试辅助工具

截图辅助工具

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');

表单辅助工具

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();

表格辅助工具

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服务集成

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

提供所有页面对象的实例:

test('使用页面对象', async ({ pageObjects }) => {
  await pageObjects.loginPage.navigate();
  await pageObjects.loginPage.login('admin', 'password');
  await pageObjects.dashboardPage.waitForLoad();
});

helpers

提供所有辅助工具的实例:

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

提供预定义的测试数据:

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服务管理器:

test('使用Mock服务', async ({ mockManager }) => {
  mockManager.addMockResponse({
    url: '/api/test',
    method: 'GET',
    response: { code: 200, data: 'mock data' }
  });
});

testConfig

提供测试配置:

test('使用测试配置', async ({ testConfig }) => {
  console.log(testConfig.name); // 环境名称
  console.log(testConfig.baseURL); // 基础URL
  console.log(testConfig.mockEnabled); // Mock是否启用
});

testLogger

提供测试日志记录器:

test('使用测试日志', async ({ testLogger }) => {
  testLogger.info('测试信息');
  testLogger.error('测试错误', error);
});

testReporter

提供测试报告生成器:

test('使用测试报告', async ({ testReporter }) => {
  testReporter.recordTestResult({
    testName: '测试名称',
    status: 'passed',
    duration: 1000
  });
});

最佳实践

1. 测试用例组织

  • 使用 test.describe 组织相关的测试用例
  • 使用 test.beforeEachtest.afterEach 设置测试前置和后置条件
  • 为每个测试用例提供清晰的描述
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. 页面对象使用

  • 始终使用页面对象而不是直接操作页面元素
  • 将页面选择器封装在页面对象中
  • 在页面对象中实现业务逻辑方法
// 好的做法
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. 测试数据管理

  • 使用测试数据生成器创建测试数据
  • 避免硬编码测试数据
  • 使用测试夹具提供的预定义数据
// 好的做法
const userData = testDataGenerator.generateUserData({
  username: 'testuser',
  status: 'active'
});

// 不好的做法
const userData = {
  username: 'testuser',
  password: 'password',
  email: 'test@example.com',
  // ... 硬编码的数据
};

4. 错误处理

  • 在测试用例中使用 try-catch 捕获错误
  • 使用测试日志记录错误信息
  • 在测试失败时截图
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 的自动等待机制
// 好的做法
await pageObjects.dashboardPage.waitForLoad();
await page.waitForSelector('.element', { state: 'visible' });

// 不好的做法
await page.waitForTimeout(5000);

6. 断言使用

  • 使用 Playwright 的 expect 断言
  • 提供有意义的断言消息
  • 验证关键的业务逻辑
// 好的做法
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. 实现页面特定的方法和选择器
import { BasePage } from './base-page';

export class NewPage extends BasePage {
  private readonly selectors = {
    // 页面选择器
  };

  constructor(page: Page) {
    super(page);
  }

  // 页面方法
}

添加新的辅助工具

  1. helpers/ 目录下创建新的辅助工具类
  2. 实现辅助工具方法
  3. 在测试夹具中注册新的辅助工具
export class NewHelper {
  private page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  // 辅助工具方法
}

添加新的测试数据生成器

  1. test-data.ts 中添加新的数据生成方法
  2. 定义数据接口
  3. 实现数据生成逻辑
export interface NewData {
  // 数据接口
}

generateNewData(overrides: Partial<NewData> = {}): NewData {
  // 数据生成逻辑
}

性能优化

并行执行

Playwright 默认支持并行执行测试,可以通过配置文件调整:

export default defineConfig({
  workers: 4, // 并发工作进程数
  fullyParallel: true, // 完全并行执行
});

测试隔离

确保每个测试用例都是独立的,避免测试之间的依赖:

test.beforeEach(async ({ page }) => {
  // 清理测试数据
  // 重置测试状态
});

重试机制

配置测试失败时的重试次数:

export default defineConfig({
  retries: 2, // 失败时重试次数
});

持续集成

CI/CD集成

在CI/CD流程中集成E2E测试:

# .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测试,确保应用的质量和稳定性。