完成自动化测试套件实施(W1-W11)

W1-W3: 基线修复与测试基础设施搭建
- 修复 Jenkins JDK 21 兼容性,统一 E2E 目录,修复 storageState 冲突
- 搭建后端测试基类 BaseContractTest + Testcontainers PostgreSQL
- 创建 TestDataFactory 链式构造,完善 Vitest 基座与 Playwright fixtures
- 建立 docker-compose.test.yml 与测试数据隔离方案

W4-W5: 单元测试补齐(阶段 2)
- 补齐 gym-member/gym-groupCourse/gym-checkIn/gym-payment 核心模块单元测试
- 补齐 gym-coach/manage-sys 模块单元测试
- 前端 utils/composables/stores 单元测试,37 文件 502 项测试
- JaCoCo 覆盖率门禁从 30% 调整至 55%,21 模块全部通过

W6-W7: 集成与契约测试(阶段 3)
- Repository 集成测试:会员/团课/签到/支付关键表,Testcontainers 100% 通过
- Handler 集成测试:WebTestClient 覆盖正向/异常/权限路径
- 网关集成测试:JWT/RBAC/签名/限流/重试
- Flyway 迁移测试:验证迁移脚本可重复执行
- OpenAPI 契约测试:覆盖 ≥80% P0 接口,202 项契约测试 0 失败
- 跨模块契约测试:会员-支付-团课数据一致性

W8-W9: E2E 与用户旅程测试(阶段 4)
- 管理员 Web 核心流程 E2E:用户/角色/菜单/字典/配置
- 小程序会员端核心页面 E2E:购卡/预约/签到
- 5 条 P0 用户旅程全链路自动化,60 条 journey 测试 0 失败

W10: 变异测试与质量门禁(阶段 5)
- 后端 PIT 配置:pitest-maven 1.19.1 + JUnit 5,覆盖率阈值 55%/变异阈值 45%
- P0 模块基线:manage-sys 48%,gym-member 30%,gym-payment 36%
- 前端 StrykerJS 配置:utils/stores 变异测试,dateFormat.ts 70.83%
- Jenkins 质量门禁:JaCoCo/PIT/E2E 统一检查,不达标阻断构建

W11: 持续运行与改进(阶段 6)
- 测试指标收集脚本 scripts/collect-test-metrics.py + HTML 看板生成器
- Flaky Test 治理 SOP:检测→隔离→根因分析→修复→验证闭环
- 测试资产定期评审流程:月度/季度/事件驱动三级机制
- 快速参考指南 docs/testing/quick-reference.md
- 累计 10 份测试文档,7 个里程碑全部达成
This commit was merged in pull request #54.
This commit is contained in:
2026-08-02 08:28:37 +08:00
parent dc68581c5e
commit 015cb0dc78
119 changed files with 21873 additions and 553 deletions
+10 -10
View File
@@ -103,7 +103,7 @@ async function globalSetup(config: FullConfig) {
if (backendAlreadyRunning) {
console.log('✅ 后端服务已在运行,跳过启动');
} else {
const backendDir = path.resolve(__dirname, '../../novalon-manage-api/manage-app');
const backendDir = path.resolve(__dirname, '../../gym-manage-api/manage-app');
const jarFile = path.join(backendDir, 'target/manage-app-1.0.0.jar');
let backendCommand: string;
@@ -116,7 +116,7 @@ async function globalSetup(config: FullConfig) {
backendArgs = [
'-jar',
jarFile,
'--spring.profiles.active=test',
'--spring.profiles.active=e2e',
'-Xms256m',
'-Xmx512m'
];
@@ -124,7 +124,7 @@ async function globalSetup(config: FullConfig) {
console.log('📦 使用Maven启动后端服务...');
console.log(' 提示: 运行 "mvn clean package -DskipTests" 构建JAR文件以获得更快的启动速度');
backendCommand = 'mvn';
backendArgs = ['spring-boot:run', '-Dspring-boot.run.profiles=test'];
backendArgs = ['spring-boot:run', '-Dspring-boot.run.profiles=e2e'];
}
console.log(` 目录: ${backendDir}`);
@@ -133,9 +133,9 @@ async function globalSetup(config: FullConfig) {
backendProcess = spawn(backendCommand, backendArgs, {
cwd: backendDir,
stdio: 'pipe',
shell: true,
shell: false,
detached: false,
env: { ...process.env, SPRING_PROFILES_ACTIVE: 'test' }
env: process.env
});
if (backendProcess.stdout) {
@@ -174,7 +174,7 @@ async function globalSetup(config: FullConfig) {
if (gatewayAlreadyRunning) {
console.log('✅ 网关服务已在运行,跳过启动');
} else {
const gatewayDir = path.resolve(__dirname, '../../novalon-manage-api/manage-gateway');
const gatewayDir = path.resolve(__dirname, '../../gym-manage-api/manage-gateway');
const gatewayJarFile = path.join(gatewayDir, 'target/manage-gateway-1.0.0.jar');
let gatewayCommand: string;
@@ -204,9 +204,9 @@ async function globalSetup(config: FullConfig) {
gatewayProcess = spawn(gatewayCommand, gatewayArgs, {
cwd: gatewayDir,
stdio: 'pipe',
shell: true,
shell: false,
detached: false,
env: { ...process.env, SPRING_PROFILES_ACTIVE: 'dev' }
env: process.env
});
if (gatewayProcess.stdout) {
@@ -299,7 +299,7 @@ async function verifyAllServices(): Promise<void> {
}
async function waitForBackendReady(): Promise<void> {
const maxRetries = 90;
const maxRetries = 180;
const retryInterval = 1000;
for (let i = 0; i < maxRetries; i++) {
@@ -347,7 +347,7 @@ async function waitForBackendReady(): Promise<void> {
}
async function waitForGatewayReady(): Promise<void> {
const maxRetries = 90;
const maxRetries = 180;
const retryInterval = 1000;
for (let i = 0; i < maxRetries; i++) {