08ea5fbe98
添加用户管理视图、API和状态管理文件
38 KiB
38 KiB
自动化测试流程框架设计文档
文档版本: v1.0
创建日期: 2026-03-28
创建者: 张翔(全栈质量保障与效能工程师)
设计方法: Brainstorming + Systematic Design
📋 文档目录
概述
背景
Everything Is Suitable项目是一个包含管理后台(Admin)、移动端应用(Uniapp)和后端API服务的综合性系统。当前项目已具备基础的E2E测试框架,但缺乏系统化的测试流程管理、智能化的测试执行策略和完善的缺陷管理闭环。
设计目标
本设计旨在建立一套完整的自动化测试流程框架,核心目标是提升测试效率,具体包括:
- 自动化执行:减少人工干预,实现测试全流程自动化
- 快速反馈:缩短测试周期,加速发布节奏
- 智能选择:精准测试,只执行相关测试用例
- 闭环管理:与企业微信智能表格集成,实现缺陷全生命周期管理
适用范围
本框架适用于:
- ✅ 端到端测试(E2E Testing)
- ✅ 用户验收测试(UAT)
- ✅ API集成测试
- ✅ 持续集成/持续部署(CI/CD)流程
设计目标与约束
核心目标
| 目标 | 描述 | 优先级 |
|---|---|---|
| 提升测试效率 | 自动化执行、快速反馈、减少人工测试时间 | P0 |
| 环境一致性 | 容器化测试环境,确保测试环境与生产环境一致 | P0 |
| 精准测试 | 智能选择测试用例,避免执行不必要的测试 | P1 |
| 完整报告 | 多层次报告体系,支持实时、汇总、趋势分析 | P1 |
| 闭环管理 | 缺陷自动跟踪,与企业微信智能表格集成 | P2 |
约束条件
| 约束 | 说明 | 影响 |
|---|---|---|
| 数据库复用 | 复用已有的postgresql_dev容器 | 减少环境启动时间,但需注意数据隔离 |
| 企业微信集成 | 使用企业微信智能表格管理缺陷 | 需要配置企业微信Webhook和表格权限 |
| Woodpecker CI | 使用Woodpecker CI作为CI/CD工具 | 需要适配Woodpecker CI的配置格式 |
| Playwright | 使用Playwright作为E2E测试框架 | 需要遵循Playwright的最佳实践 |
总体架构设计
架构图
┌─────────────────────────────────────────────────────────────────┐
│ 自动化测试流程框架 │
│ (智能执行 + 容器化环境) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌──────▼──────┐ ┌────▼────┐ ┌─────▼──────┐
│ 测试环境层 │ │测试执行层│ │ 测试管理层 │
└──────┬──────┘ └────┬────┘ └─────┬──────┘
│ │ │
┌───────────┼───────────┐ │ ┌───────────┼───────────┐
│ │ │ │ │ │ │
┌───▼───┐ ┌────▼────┐ ┌───▼───▼───┐ ┌────▼────┐ ┌───▼────┐
│Docker │ │PostgreSQL│ │智能测试 │ │多层次 │ │缺陷管理│
│容器 │ │(复用) │ │选择器 │ │报告体系 │ │(企微) │
└───────┘ └─────────┘ └──────────┘ └─────────┘ └────────┘
核心组件
1. 测试环境层
- Docker容器:前端应用、后端API容器化部署
- PostgreSQL数据库:复用postgresql_dev容器
- 网络隔离:独立的test-network网络
2. 测试执行层
- 智能测试选择器:基于代码变更分析,精准选择测试用例
- 测试执行引擎:Playwright并行执行
- 测试数据管理:统一的测试数据生成和管理
3. 测试管理层
- 多层次报告体系:实时报告、汇总报告、趋势报告
- 缺陷管理系统:与企业微信智能表格集成
- 通知系统:企业微信群机器人通知
触发机制
| 触发场景 | 执行策略 | 测试范围 |
|---|---|---|
| 代码提交 | 智能选择 | 变更相关的测试用例 |
| Pull Request | 智能选择 | 变更相关的测试用例 |
| 定时任务(每日凌晨2点) | 全量执行 | 所有测试用例 |
| 手动触发 | 可配置 | 指定模块或用例 |
测试环境配置标准
容器化测试环境架构
Docker Compose配置
# docker-compose.test.yml
version: '3.8'
services:
# 前端应用(测试环境)
admin-frontend-test:
build:
context: ./everything-is-suitable-admin
dockerfile: Dockerfile.test
container_name: admin-frontend-test
ports:
- "5174:5174"
environment:
- NODE_ENV=test
- VITE_API_BASE_URL=http://admin-api-test:8082
- VITE_MOCK_ENABLED=false
depends_on:
admin-api-test:
condition: service_healthy
networks:
- test-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5174"]
interval: 10s
timeout: 5s
retries: 3
# 后端API(测试环境)
admin-api-test:
build:
context: ./everything-is-suitable-api/everything-is-suitable-admin-app
dockerfile: Dockerfile
container_name: admin-api-test
ports:
- "8083:8082"
environment:
- SPRING_PROFILES_ACTIVE=test
- SPRING_R2DBC_URL=r2dbc:postgresql://host.docker.internal:5432/everything_suitable_test
- SPRING_R2DBC_USERNAME=${DB_USERNAME:-postgres}
- SPRING_R2DBC_PASSWORD=${DB_PASSWORD:-postgres}
networks:
- test-network
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8082/actuator/health"]
interval: 10s
timeout: 5s
retries: 5
networks:
test-network:
driver: bridge
环境配置说明
| 配置项 | 测试环境 | 开发环境 | 说明 |
|---|---|---|---|
| 前端端口 | 5174 | 5173 | 避免端口冲突 |
| API端口 | 8083 | 8082 | 避免端口冲突 |
| 数据库 | postgresql_dev | postgresql_dev | 复用已有容器 |
| 数据库名 | everything_suitable_test | everything_suitable | 使用独立测试数据库 |
| Mock服务 | 禁用 | 启用 | 测试环境使用真实API |
环境启动流程
1. 检查postgresql_dev容器状态
↓
2. 创建测试数据库(如果不存在)
↓
3. 启动后端API容器
↓
4. 等待API健康检查通过
↓
5. 启动前端应用容器
↓
6. 等待前端健康检查通过
↓
7. 环境就绪,开始测试
数据库管理策略
测试数据隔离
-- 创建测试数据库
CREATE DATABASE everything_suitable_test;
-- 使用独立的Schema
CREATE SCHEMA test_data;
-- 测试前清理数据
TRUNCATE TABLE test_data.users CASCADE;
TRUNCATE TABLE test_data.roles CASCADE;
数据初始化脚本
// scripts/init-test-data.ts
export async function initTestData() {
// 创建测试用户
await createTestUsers([
{ username: 'admin', password: 'admin123', role: 'admin' },
{ username: 'user1', password: 'user123', role: 'user' },
]);
// 创建测试角色
await createTestRoles([
{ name: '管理员', code: 'admin', permissions: ['all'] },
{ name: '普通用户', code: 'user', permissions: ['read'] },
]);
// 创建测试菜单
await createTestMenus([
{ name: '用户管理', path: '/user-management' },
{ name: '角色管理', path: '/role-management' },
]);
}
测试用例设计规范
测试用例组织结构
e2e/
├── user-management/ # 用户管理模块
│ ├── create-user.spec.ts # 创建用户
│ ├── edit-user.spec.ts # 编辑用户
│ ├── delete-user.spec.ts # 删除用户
│ ├── batch-operations.spec.ts # 批量操作
│ └── user-search.spec.ts # 用户搜索
├── role-management/ # 角色管理模块
│ ├── create-role.spec.ts
│ ├── edit-role.spec.ts
│ ├── assign-permissions.spec.ts
│ └── delete-role.spec.ts
├── menu-management/ # 菜单管理模块
│ ├── create-menu.spec.ts
│ ├── edit-menu.spec.ts
│ └── delete-menu.spec.ts
├── almanac-functionality/ # 黄历功能模块
│ ├── search-almanac.spec.ts
│ ├── view-detail.spec.ts
│ ├── favorite.spec.ts
│ └── share.spec.ts
└── api/ # API集成测试
├── user-api.spec.ts
├── role-api.spec.ts
└── menu-api.spec.ts
测试用例命名规范
文件命名
- 格式:
{操作}-{对象}.spec.ts - 示例:
create-user.spec.ts、edit-role.spec.ts - 使用小写字母和连字符
测试用例命名
- 格式:
{操作} - {场景} @{优先级} @{模块} @{功能} - 示例:
创建用户 - 核心流程 @smoke @p0 @user-management @create
标签体系
优先级标签
| 标签 | 说明 | 执行频率 | 预期耗时 |
|---|---|---|---|
@smoke |
冒烟测试 | 每次提交 | < 5分钟 |
@functional |
功能测试 | 每次提交 | < 30分钟 |
@edge |
边缘场景 | 每日构建 | < 2小时 |
重要性标签
| 标签 | 说明 | 失败影响 |
|---|---|---|
@p0 |
核心功能 | 阻塞发布 |
@p1 |
重要功能 | 需要修复 |
@p2 |
次要功能 | 可延后修复 |
模块标签
| 标签 | 说明 |
|---|---|
@user-management |
用户管理模块 |
@role-management |
角色管理模块 |
@menu-management |
菜单管理模块 |
@almanac-functionality |
黄历功能模块 |
@api |
API集成测试 |
功能标签
| 标签 | 说明 |
|---|---|
@create |
创建操作 |
@edit |
编辑操作 |
@delete |
删除操作 |
@validation |
验证功能 |
@search |
搜索功能 |
测试用例模板
// e2e/user-management/create-user.spec.ts
import { test, expect } from '../test-fixtures';
test.describe('用户管理 - 创建用户', () => {
test.beforeEach(async ({ pageObjects, testData }) => {
// 前置条件:登录系统
await pageObjects.loginPage.navigate();
await pageObjects.loginPage.login(
testData.admin.username,
testData.admin.password
);
});
test('创建用户 - 核心流程 @smoke @p0 @user-management @create', async ({
pageObjects,
testData,
testLogger
}) => {
testLogger.startTest('创建用户 - 核心流程');
try {
// 1. 导航到用户管理页面
await pageObjects.userManagementPage.navigate();
// 2. 点击创建用户按钮
await pageObjects.userManagementPage.clickCreateButton();
// 3. 填写用户信息
const newUser = testDataGenerator.generateUserData();
await pageObjects.userManagementPage.fillUserForm(newUser);
// 4. 提交表单
await pageObjects.userManagementPage.submitForm();
// 5. 验证创建成功
const successMessage = await pageObjects.userManagementPage.getSuccessMessage();
expect(successMessage).toContain('创建成功');
// 6. 验证用户列表中存在新用户
const userExists = await pageObjects.userManagementPage.searchUser(newUser.username);
expect(userExists).toBeTruthy();
testLogger.endTest('创建用户 - 核心流程', 'passed');
} catch (error) {
testLogger.endTest('创建用户 - 核心流程', 'failed', error as Error);
throw error;
}
});
test('创建用户 - 必填字段验证 @functional @p1 @user-management @create @validation', async ({
pageObjects
}) => {
// 测试逻辑
});
test('创建用户 - 重复用户名验证 @functional @p1 @user-management @create @validation', async ({
pageObjects
}) => {
// 测试逻辑
});
test('创建用户 - 边缘场景:超长用户名 @edge @p2 @user-management @create', async ({
pageObjects
}) => {
// 测试逻辑
});
});
智能测试选择器
设计原理
智能测试选择器通过分析代码变更,精准选择需要执行的测试用例,避免执行不必要的测试,从而提升测试效率。
核心组件
1. 代码-测试映射配置
// config/test-mapping.config.ts
export interface TestMapping {
[sourceFile: string]: {
tests: string[];
priority: 'high' | 'medium' | 'low';
modules: string[];
};
}
export const testMapping: TestMapping = {
// 用户管理模块
'everything-is-suitable-admin/src/views/UserManagement.vue': {
tests: [
'e2e/user-management/*.spec.ts',
],
priority: 'high',
modules: ['user-management'],
},
'everything-is-suitable-admin/src/api/user.ts': {
tests: [
'e2e/user-management/*.spec.ts',
'e2e/api/user-api.spec.ts',
],
priority: 'high',
modules: ['user-management', 'api'],
},
'everything-is-suitable-admin/src/stores/user.ts': {
tests: [
'e2e/user-management/*.spec.ts',
],
priority: 'medium',
modules: ['user-management'],
},
// 角色管理模块
'everything-is-suitable-admin/src/views/RoleManagement.vue': {
tests: [
'e2e/role-management/*.spec.ts',
],
priority: 'high',
modules: ['role-management'],
},
'everything-is-suitable-admin/src/api/role.ts': {
tests: [
'e2e/role-management/*.spec.ts',
'e2e/api/role-api.spec.ts',
],
priority: 'high',
modules: ['role-management', 'api'],
},
// 黄历功能模块
'everything-is-suitable-uniapp/src/pages/almanac/index.vue': {
tests: [
'e2e/almanac-functionality/*.spec.ts',
],
priority: 'high',
modules: ['almanac-functionality'],
},
};
// 反向映射:模块 -> 测试文件
export const moduleToTests: Record<string, string[]> = {
'user-management': ['e2e/user-management/*.spec.ts'],
'role-management': ['e2e/role-management/*.spec.ts'],
'menu-management': ['e2e/menu-management/*.spec.ts'],
'almanac-functionality': ['e2e/almanac-functionality/*.spec.ts'],
'api': ['e2e/api/*.spec.ts'],
};
2. 智能测试选择器核心实现
// scripts/smart-test-selector.ts
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { testMapping, moduleToTests } from '../config/test-mapping.config';
export interface TestSelectionResult {
selectedTests: string[];
affectedModules: string[];
changedFiles: string[];
analysisReport: string;
}
export class SmartTestSelector {
private projectRoot: string;
constructor(projectRoot: string = process.cwd()) {
this.projectRoot = projectRoot;
}
/**
* 根据代码变更选择测试用例
*/
selectTestsByChanges(
changedFiles: string[],
options: {
includeRelated?: boolean;
priority?: 'high' | 'medium' | 'low' | 'all';
testLevel?: 'smoke' | 'functional' | 'all';
} = {}
): TestSelectionResult {
const {
includeRelated = true,
priority = 'all',
testLevel = 'all',
} = options;
const selectedTests = new Set<string>();
const affectedModules = new Set<string>();
// 分析每个变更文件
for (const file of changedFiles) {
const normalizedPath = this.normalizePath(file);
const mapping = this.findMapping(normalizedPath);
if (mapping) {
// 添加直接关联的测试
mapping.tests.forEach(test => selectedTests.add(test));
mapping.modules.forEach(module => affectedModules.add(module));
// 如果启用关联分析,添加相关模块的测试
if (includeRelated) {
this.addRelatedTests(mapping.modules, selectedTests, affectedModules);
}
}
}
// 根据优先级过滤
const filteredTests = this.filterByPriority(
Array.from(selectedTests),
priority
);
// 根据测试级别过滤
const finalTests = this.filterByTestLevel(filteredTests, testLevel);
// 生成分析报告
const analysisReport = this.generateAnalysisReport({
changedFiles,
affectedModules: Array.from(affectedModules),
selectedTests: finalTests,
});
return {
selectedTests: finalTests,
affectedModules: Array.from(affectedModules),
changedFiles,
analysisReport,
};
}
/**
* 从Git获取变更文件
*/
getChangedFilesFromGit(
baseBranch: string = 'origin/main',
headBranch: string = 'HEAD'
): string[] {
try {
const output = execSync(
`git diff --name-only ${baseBranch}...${headBranch}`,
{ encoding: 'utf-8', cwd: this.projectRoot }
);
return output
.split('\n')
.filter(file => file.trim() && this.isSourceFile(file));
} catch (error) {
console.error('Failed to get changed files from git:', error);
return [];
}
}
/**
* 规范化文件路径
*/
private normalizePath(filePath: string): string {
return filePath.replace(/\\/g, '/').replace(/^\.\//, '');
}
/**
* 查找文件对应的测试映射
*/
private findMapping(normalizedPath: string) {
// 精确匹配
if (testMapping[normalizedPath]) {
return testMapping[normalizedPath];
}
// 模糊匹配(支持通配符)
for (const [pattern, mapping] of Object.entries(testMapping)) {
if (this.matchPattern(normalizedPath, pattern)) {
return mapping;
}
}
return null;
}
/**
* 简单的模式匹配
*/
private matchPattern(path: string, pattern: string): boolean {
const regex = new RegExp(
'^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$'
);
return regex.test(path);
}
/**
* 添加相关测试
*/
private addRelatedTests(
modules: string[],
selectedTests: Set<string>,
affectedModules: Set<string>
): void {
for (const module of modules) {
const relatedTests = moduleToTests[module];
if (relatedTests) {
relatedTests.forEach(test => selectedTests.add(test));
}
}
}
/**
* 根据优先级过滤测试
*/
private filterByPriority(
tests: string[],
priority: 'high' | 'medium' | 'low' | 'all'
): string[] {
if (priority === 'all') {
return tests;
}
const priorityMap = {
high: ['@p0', '@smoke'],
medium: ['@p1', '@functional'],
low: ['@p2', '@edge'],
};
const targetTags = priorityMap[priority];
return tests.filter(test =>
targetTags.some(tag => test.includes(tag))
);
}
/**
* 根据测试级别过滤
*/
private filterByTestLevel(
tests: string[],
level: 'smoke' | 'functional' | 'all'
): string[] {
if (level === 'all') {
return tests;
}
const levelMap = {
smoke: '@smoke',
functional: '@functional',
};
const targetTag = levelMap[level];
return tests.filter(test => test.includes(targetTag));
}
/**
* 判断是否为源代码文件
*/
private isSourceFile(filePath: string): boolean {
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.vue', '.java'];
return extensions.some(ext => filePath.endsWith(ext));
}
/**
* 生成分析报告
*/
private generateAnalysisReport(data: {
changedFiles: string[];
affectedModules: string[];
selectedTests: string[];
}): string {
return `
# 智能测试选择分析报告
## 变更文件 (${data.changedFiles.length}个)
${data.changedFiles.map(f => `- ${f}`).join('\n')}
## 受影响模块 (${data.affectedModules.length}个)
${data.affectedModules.map(m => `- ${m}`).join('\n')}
## 选中测试用例 (${data.selectedTests.length}个)
${data.selectedTests.map(t => `- ${t}`).join('\n')}
## 执行建议
- 优先执行冒烟测试(@smoke标签)
- 然后执行功能测试(@functional标签)
- 最后执行边缘场景测试(@edge标签)
`.trim();
}
}
工作流程
1. Git Diff获取变更文件
↓
2. 分析变更文件,查找映射关系
↓
3. 选择直接关联的测试用例
↓
4. 添加相关模块的测试用例(可选)
↓
5. 根据优先级和测试级别过滤
↓
6. 生成测试选择报告
↓
7. 执行选中的测试用例
测试执行与调度机制
Woodpecker CI集成配置
# .woodpecker.yml
variables:
- &node_image 'node:18-alpine'
- &java_image 'openjdk:17-jdk-slim'
when:
- event: [push, pull_request]
branch: [main, develop]
- event: cron
cron: [daily-test]
steps:
# 步骤1:分析代码变更
analyze-changes:
image: *node_image
commands:
- npm ci
- |
# 获取变更文件
if [ "$CI_BUILD_EVENT" = "cron" ]; then
# 定时任务:执行全量测试
echo "[]" > changed-files.txt
else
# 代码提交:获取变更文件
git diff --name-only origin/main...HEAD > changed-files.txt || echo "[]" > changed-files.txt
fi
- |
# 智能选择测试用例
node scripts/smart-test-selector.js \
--input changed-files.txt \
--output selected-tests.json \
--report test-selection-report.md
when:
event: [push, pull_request]
# 步骤2:启动测试环境
start-test-environment:
image: docker/compose:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- docker-compose -f docker-compose.test.yml up -d
- |
# 等待服务健康
echo "Waiting for services to be healthy..."
sleep 10
until curl -f http://localhost:8083/actuator/health; do
echo "Waiting for API..."
sleep 5
done
until curl -f http://localhost:5174; do
echo "Waiting for frontend..."
sleep 5
done
when:
event: [push, pull_request, cron]
# 步骤3:执行智能测试
run-smart-tests:
image: *node_image
environment:
- TEST_ENV=ci
- API_BASE_URL=http://localhost:8083
- FRONTEND_BASE_URL=http://localhost:5174
commands:
- npm ci
- npx playwright install --with-deps chromium
- |
# 读取选中的测试用例
if [ -f selected-tests.json ]; then
# 执行智能选择的测试
node scripts/run-selected-tests.js selected-tests.json
else
# 执行全量测试
npm run test:e2e
fi
depends_on:
- start-test-environment
- analyze-changes
# 步骤4:生成测试报告
generate-reports:
image: *node_image
commands:
- npm run test:report
- |
# 生成趋势报告
node scripts/generate-trend-report.js \
--input test-results \
--output test-results/trend-report.html
depends_on:
- run-smart-tests
# 步骤5:同步缺陷到企业微信智能表格
sync-defects:
image: *node_image
environment:
- WECOM_WEBHOOK_URL=${WECOM_WEBHOOK_URL}
- WECOM_TABLE_ID=${WECOM_TABLE_ID}
commands:
- |
# 分析测试失败用例
node scripts/analyze-failures.js \
--input test-results \
--output failures.json
- |
# 同步到企业微信智能表格
if [ -s failures.json ]; then
node scripts/sync-to-wecom-table.js failures.json
fi
depends_on:
- generate-reports
when:
status: [failure]
# 步骤6:发送通知
notify:
image: *node_image
environment:
- WECOM_BOT_WEBHOOK=${WECOM_BOT_WEBHOOK}
commands:
- |
# 发送测试结果通知
node scripts/send-notification.js \
--report test-results/report.html \
--webhook $WECOM_BOT_WEBHOOK
depends_on:
- generate-reports
when:
status: [success, failure]
# 步骤7:清理测试环境
cleanup:
image: docker/compose:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
commands:
- docker-compose -f docker-compose.test.yml down -v
when:
status: [success, failure]
# 定时任务配置
cron:
daily-test:
schedule: "0 2 * * *" # 每天凌晨2点
branch: main
测试执行脚本
// scripts/run-selected-tests.ts
import { execSync } from 'child_process';
import * as fs from 'fs';
import { SmartTestSelector } from './smart-test-selector';
interface SelectedTests {
smoke: string[];
functional: string[];
edge: string[];
}
export class TestExecutor {
private selector: SmartTestSelector;
constructor() {
this.selector = new SmartTestSelector();
}
/**
* 执行智能选择的测试
*/
async runSelectedTests(testsFile: string): Promise<void> {
const selectedTests: SelectedTests = JSON.parse(
fs.readFileSync(testsFile, 'utf-8')
);
console.log('=== 开始执行智能测试 ===\n');
// 1. 执行冒烟测试(优先级最高)
if (selectedTests.smoke.length > 0) {
console.log('📦 执行冒烟测试...');
await this.runTests(selectedTests.smoke, 'smoke');
}
// 2. 执行功能测试
if (selectedTests.functional.length > 0) {
console.log('📦 执行功能测试...');
await this.runTests(selectedTests.functional, 'functional');
}
// 3. 执行边缘场景测试(可选)
if (selectedTests.edge.length > 0 && process.env.RUN_EDGE_TESTS === 'true') {
console.log('📦 执行边缘场景测试...');
await this.runTests(selectedTests.edge, 'edge');
}
console.log('\n✅ 智能测试执行完成');
}
/**
* 执行指定测试用例
*/
private async runTests(
testPatterns: string[],
level: string
): Promise<void> {
for (const pattern of testPatterns) {
try {
console.log(` 执行: ${pattern}`);
execSync(
`npx playwright test "${pattern}" --project=chromium --reporter=html`,
{
stdio: 'inherit',
env: {
...process.env,
TEST_LEVEL: level,
},
}
);
} catch (error) {
console.error(` ❌ 测试失败: ${pattern}`);
// 继续执行其他测试
}
}
}
/**
* 执行全量测试
*/
async runAllTests(): Promise<void> {
console.log('=== 开始执行全量测试 ===\n');
execSync('npm run test:e2e', {
stdio: 'inherit',
});
console.log('\n✅ 全量测试执行完成');
}
}
// 主函数
async function main() {
const executor = new TestExecutor();
const testsFile = process.argv[2] || 'selected-tests.json';
if (fs.existsSync(testsFile)) {
await executor.runSelectedTests(testsFile);
} else {
await executor.runAllTests();
}
}
main().catch(console.error);
测试结果分析与报告生成
多层次报告体系
1. 实时报告
测试执行过程中实时更新,每5秒自动刷新。
// 实时报告生成
generateRealtimeReport(result: TestResult): void {
const reportPath = path.join(this.outputDir, 'realtime-report.json');
let report: TestReport;
if (fs.existsSync(reportPath)) {
report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
} else {
report = this.createEmptyReport();
}
// 更新报告数据
report.totalTests++;
report[result.status]++;
report.duration += result.duration;
// 写入报告
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
// 生成实时HTML报告
this.generateRealtimeHTML(report);
}
2. 汇总报告
测试完成后生成详细报告,包含:
- 总体统计(总数、通过、失败、跳过)
- 模块统计(每个模块的测试情况)
- 详细结果(每个测试用例的执行情况)
- 错误信息(失败用例的错误详情)
3. 趋势报告
历史测试结果对比分析,包含:
- 时间线图表(通过率趋势)
- 平均通过率
- 趋势分析(improving/stable/declining)
报告格式
| 格式 | 用途 | 生成时机 |
|---|---|---|
| HTML | 可视化查看 | 测试完成后 |
| JSON | 机器可读 | 测试完成后 |
| JUnit XML | CI集成 | 测试完成后 |
报告存储
test-results/
├── reports/
│ ├── realtime-report.html # 实时报告
│ ├── summary-report.html # 汇总报告
│ ├── summary-report.json # JSON格式报告
│ ├── junit-report.xml # JUnit格式报告
│ └── trend-report.html # 趋势报告
├── history/
│ ├── report-2026-03-28-02-00-00.json
│ ├── report-2026-03-27-02-00-00.json
│ └── ...
└── screenshots/
├── test-failure-1.png
└── test-failure-2.png
缺陷管理闭环流程
企业微信智能表格集成
1. 缺陷记录结构
| 字段 | 类型 | 说明 |
|---|---|---|
| 缺陷ID | 文本 | DEF-{timestamp} |
| 测试用例 | 文本 | 测试用例名称 |
| 模块 | 文本 | 所属模块 |
| 优先级 | 选择 | P0/P1/P2 |
| 状态 | 选择 | open/in_progress/fixed/closed |
| 错误信息 | 文本 | 错误详情 |
| 截图 | 文本 | 截图URL列表 |
| 日志 | 文本 | 日志内容 |
| 创建时间 | 日期 | 创建时间 |
| 更新时间 | 日期 | 更新时间 |
| 报告人 | 文本 | 自动化测试系统 |
| 负责人 | 文本 | 开发人员 |
| 测试运行ID | 文本 | CI构建号 |
| Git提交 | 文本 | Git Commit Hash |
| Git分支 | 文本 | Git分支名称 |
2. 缺陷状态流转
测试失败 → open(新建)
↓
开发认领 → in_progress(处理中)
↓
开发修复 → fixed(已修复)
↓
回归测试通过 → closed(已关闭)
↓
回归测试失败 → reopen(重新打开)
3. 自动化同步流程
// scripts/analyze-failures.ts
export class FailureAnalyzer {
/**
* 分析测试失败并同步到企业微信
*/
async analyzeAndSync(): Promise<void> {
const failures = this.loadFailures();
if (failures.length === 0) {
console.log('✅ 没有失败的测试用例');
return;
}
console.log(`📊 发现 ${failures.length} 个失败的测试用例`);
for (const failure of failures) {
const defect = this.createDefectRecord(failure);
await this.wecomIntegration.syncDefect(defect);
}
// 发送汇总通知
await this.sendSummaryNotification(failures);
}
}
4. 企业微信群通知
// 发送通知到企业微信群
await this.wecomIntegration.sendNotification({
title: '🚨 测试失败通知',
content: `
**测试执行完成,发现 ${failures.length} 个失败用例**
- P0(核心功能): ${p0Count} 个
- P1(重要功能): ${p1Count} 个
- P2(次要功能): ${p2Count} 个
**失败详情:**
${failures.slice(0, 5).map(f => `- [${f.module}] ${f.testName}`).join('\n')}
**查看详细报告:**
${process.env.CI_BUILD_URL}
`.trim(),
mentionedList: p0Count > 0 ? ['all'] : [],
});
质量门禁要求
代码提交阶段
| 检查项 | 要求 | 失败后果 |
|---|---|---|
| 代码风格检查 | 通过 | 阻止提交 |
| 单元测试 | 通过率 100% | 阻止提交 |
| 冒烟测试 | 通过率 100% | 阻止合并 |
Pull Request阶段
| 检查项 | 要求 | 失败后果 |
|---|---|---|
| 功能测试 | 通过率 ≥ 95% | 阻止合并 |
| 代码覆盖率 | ≥ 70% | 警告 |
| 安全扫描 | 无Critical漏洞 | 阻止合并 |
发布阶段
| 检查项 | 要求 | 失败后果 |
|---|---|---|
| 全量回归测试 | 通过率 100% | 阻止发布 |
| 性能测试 | P99延迟 < 100ms | 警告 |
| 兼容性测试 | 主流浏览器通过 | 阻止发布 |
定期检查
| 检查项 | 频率 | 要求 |
|---|---|---|
| 全量回归测试 | 每日 | 通过率 ≥ 95% |
| 性能基准测试 | 每周 | 无性能退化 |
| 安全扫描 | 每周 | 无High级别漏洞 |
实施路径
阶段1:基础框架搭建(第1-2周)
目标:建立容器化测试环境和智能测试选择器基础框架
任务清单:
- 创建docker-compose.test.yml配置文件
- 配置测试数据库(everything_suitable_test)
- 实现智能测试选择器(文件路径映射)
- 创建test-mapping.config.ts配置文件
- 编写测试执行脚本
- 集成到Woodpecker CI
交付物:
- ✅ 容器化测试环境配置
- ✅ 智能测试选择器实现
- ✅ CI/CD集成配置
- ✅ 测试用例标签规范文档
阶段2:报告体系与缺陷管理(第3-4周)
目标:建立多层次报告体系和企业微信智能表格集成
任务清单:
- 实现报告生成器(实时、汇总、趋势)
- 创建HTML、JSON、JUnit报告模板
- 实现企业微信智能表格集成
- 编写缺陷分析和同步脚本
- 配置企业微信群机器人通知
- 建立缺陷状态流转机制
交付物:
- ✅ 多层次报告生成器
- ✅ 企业微信智能表格集成
- ✅ 缺陷管理闭环流程
- ✅ 通知系统配置
阶段3:优化与完善(第5-6周)
目标:优化智能测试选择器,提升精确度
任务清单:
- 引入代码覆盖率分析
- 自动生成和维护代码-测试映射关系
- 添加历史失败率分析
- 实现可视化工具查看依赖关系
- 优化测试执行性能
- 完善文档和培训材料
交付物:
- ✅ 优化的智能测试选择器
- ✅ 可视化依赖关系工具
- ✅ 完整的使用文档
- ✅ 团队培训材料
阶段4:持续优化(长期)
目标:持续优化测试框架,提升测试效率
优化方向:
- 根据实际使用情况调整策略
- 添加更多智能特性
- 提升测试覆盖率
- 优化测试执行速度
- 扩展测试类型(性能测试、安全测试)
附录
A. 配置文件模板
A.1 环境变量配置
# .env.test
NODE_ENV=test
TEST_ENV=ci
# 测试环境URL
API_BASE_URL=http://localhost:8083
FRONTEND_BASE_URL=http://localhost:5174
# 数据库配置
DB_HOST=localhost
DB_PORT=5432
DB_NAME=everything_suitable_test
DB_USERNAME=postgres
DB_PASSWORD=postgres
# 企业微信配置
WECOM_WEBHOOK_URL=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY
WECOM_TABLE_ID=YOUR_TABLE_ID
WECOM_BOT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_BOT_KEY
A.2 Playwright配置
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 4 : undefined,
reporter: [
['html', { outputFolder: 'test-results/reports' }],
['json', { outputFile: 'test-results/reports/results.json' }],
['junit', { outputFile: 'test-results/reports/junit.xml' }],
],
use: {
baseURL: process.env.FRONTEND_BASE_URL || 'http://localhost:5174',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' },
},
],
webServer: {
command: 'npm run dev:test',
url: 'http://localhost:5174',
reuseExistingServer: !process.env.CI,
},
});
B. 常用命令
B.1 本地测试命令
# 启动测试环境
docker-compose -f docker-compose.test.yml up -d
# 运行智能测试
npm run test:smart
# 运行全量测试
npm run test:e2e
# 运行特定模块测试
npx playwright test e2e/user-management/
# 运行冒烟测试
npx playwright test --grep @smoke
# 生成测试报告
npm run test:report
B.2 CI/CD命令
# 手动触发测试
woodpecker-cli build start --branch main
# 查看测试结果
woodpecker-cli log view <build-number>
# 下载测试报告
woodpecker-cli artifact download <build-number> test-results/
C. 故障排查
C.1 常见问题
问题1:测试环境启动失败
- 检查Docker是否正常运行
- 检查端口5174、8083是否被占用
- 检查postgresql_dev容器是否运行
问题2:智能测试选择器无法找到映射关系
- 检查test-mapping.config.ts配置是否正确
- 检查文件路径是否匹配
- 查看分析报告确认变更文件
问题3:企业微信通知发送失败
- 检查Webhook URL是否正确
- 检查网络连接是否正常
- 检查企业微信机器人权限
C.2 日志查看
# 查看测试环境日志
docker-compose -f docker-compose.test.yml logs -f
# 查看测试执行日志
cat test-results/logs/test-execution.log
# 查看CI/CD日志
woodpecker-cli log view <build-number>
总结
本自动化测试流程框架设计文档详细描述了一套完整的测试解决方案,包括:
- 容器化测试环境:确保环境一致性,快速启动
- 智能测试选择:精准测试,提升效率
- 模块化组织:按业务模块组织测试用例
- 多层次报告:实时、汇总、趋势三层报告体系
- 闭环管理:与企业微信智能表格集成,自动化缺陷跟踪
通过本框架的实施,可以显著提升测试效率,缩短测试周期,加速发布节奏,确保产品质量。
文档版本历史:
| 版本 | 日期 | 修改内容 | 修改人 |
|---|---|---|---|
| v1.0 | 2026-03-28 | 初始版本 | 张翔 |
联系方式:
如有问题或建议,请联系:
- 文档作者:张翔
- 邮箱:zhangxiang@example.com
- 企业微信:张翔