添加用户管理视图、API和状态管理文件
32 KiB
测试套件修复计划
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: 修复测试套件中的关键问题,使整体测试通过率达到95%以上,E2E测试可正常运行,符合金融级测试标准。
Architecture: 采用分层修复策略,从基础设施配置开始,逐步修复依赖管理和测试隔离问题,最后优化测试覆盖率和稳定性。
Tech Stack: Playwright (E2E), Vitest (前端单元测试), pytest (API测试), TypeScript, Python
执行策略
本计划按照优先级分为三个阶段:
- P0阶段:修复阻塞测试运行的基础设施问题(预计2-3小时)
- P1阶段:提升测试稳定性和通过率(预计4-6小时)
- P2阶段:优化测试架构和覆盖率(预计8-12小时)
每个任务都遵循TDD原则:先写失败测试,再实现最小化修复,最后验证通过。
P0阶段:基础设施修复(立即执行)
Task 1: 创建Playwright配置文件
Files:
- Create:
everything-is-suitable-admin/playwright.config.ts
Step 1: 创建配置文件基础结构
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['list'],
['json', { outputFile: 'test-results/results.json' }]
],
use: {
baseURL: process.env.E2E_BASE_URL || 'http://localhost:5174',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:5174',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
Step 2: 验证配置文件语法
Run: cd everything-is-suitable-admin && npx playwright test --config=playwright.config.ts --dry-run
Expected: 配置文件语法正确,无错误输出
Step 3: 运行单个E2E测试验证配置
Run: cd everything-is-suitable-admin && npx playwright test e2e/auth.spec.ts:89 --reporter=list
Expected: 测试开始执行(可能失败,但配置生效)
Step 4: 提交配置文件
git add everything-is-suitable-admin/playwright.config.ts
git commit -m "feat: add Playwright configuration for E2E tests"
Task 2: 修复E2E测试中的相对路径问题
Files:
- Modify:
everything-is-suitable-admin/e2e/auth.spec.ts:86 - Modify:
everything-is-suitable-admin/e2e/user.spec.ts - Modify:
everything-is-suitable-admin/e2e/role.spec.ts - Modify:
everything-is-suitable-admin/e2e/menu.spec.ts
Step 1: 修改auth.spec.ts中的导航路径
查找所有 page.goto('/login') 并修改为:
// 修改前
await page.goto('/login');
// 修改后
await page.goto('/login');
注意:由于配置了baseURL,相对路径应该可以工作。如果仍有问题,使用绝对路径:
await page.goto('http://localhost:5174/login');
Step 2: 检查其他测试文件的路径
Run: cd everything-is-suitable-admin && grep -n "page.goto" e2e/*.spec.ts
Expected: 列出所有page.goto调用
Step 3: 统一修改所有测试文件的路径
根据grep结果,逐个文件修改路径问题。
Step 4: 运行E2E测试验证修复
Run: cd everything-is-suitable-admin && npx playwright test e2e/auth.spec.ts --reporter=list
Expected: 至少第一个测试应该能够加载登录页面
Step 5: 提交修复
git add everything-is-suitable-admin/e2e/
git commit -m "fix: resolve navigation path issues in E2E tests"
Task 3: 创建缺失的前端测试工具模块
Files:
- Create:
everything-is-suitable-admin/src/utils/formRules.ts - Create:
everything-is-suitable-admin/src/utils/formValidator.ts - Create:
everything-is-suitable-admin/src/utils/passwordValidator.ts
Step 1: 创建formRules.ts
import { Rule } from 'ant-design-vue/es/form';
export const formRules: Record<string, Rule[]> = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在3到20个字符', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 6, message: '密码长度不能少于6个字符', trigger: 'blur' },
],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入有效的邮箱地址', trigger: 'blur' },
],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入有效的手机号', trigger: 'blur' },
],
};
export default formRules;
Step 2: 创建formValidator.ts
export interface ValidationResult {
valid: boolean;
message?: string;
}
export const formValidator = {
username: (value: string): ValidationResult => {
if (!value) {
return { valid: false, message: '用户名不能为空' };
}
if (value.length < 3 || value.length > 20) {
return { valid: false, message: '用户名长度在3到20个字符' };
}
return { valid: true };
},
email: (value: string): ValidationResult => {
if (!value) {
return { valid: false, message: '邮箱不能为空' };
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
return { valid: false, message: '请输入有效的邮箱地址' };
}
return { valid: true };
},
phone: (value: string): ValidationResult => {
if (!value) {
return { valid: false, message: '手机号不能为空' };
}
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(value)) {
return { valid: false, message: '请输入有效的手机号' };
}
return { valid: true };
},
required: (value: any, fieldName: string = '字段'): ValidationResult => {
if (value === null || value === undefined || value === '') {
return { valid: false, message: `${fieldName}不能为空` };
}
return { valid: true };
},
};
export default formValidator;
Step 3: 创建passwordValidator.ts
export interface PasswordValidationResult {
valid: boolean;
score: number;
message?: string;
suggestions?: string[];
}
export const passwordValidator = {
validate: (password: string): PasswordValidationResult => {
if (!password) {
return {
valid: false,
score: 0,
message: '密码不能为空',
};
}
let score = 0;
const suggestions: string[] = [];
if (password.length >= 8) score += 1;
else suggestions.push('密码长度至少8个字符');
if (password.length >= 12) score += 1;
else suggestions.push('建议使用12个字符以上的密码');
if (/[a-z]/.test(password)) score += 1;
else suggestions.push('添加小写字母');
if (/[A-Z]/.test(password)) score += 1;
else suggestions.push('添加大写字母');
if (/[0-9]/.test(password)) score += 1;
else suggestions.push('添加数字');
if (/[^a-zA-Z0-9]/.test(password)) score += 1;
else suggestions.push('添加特殊字符');
const valid = score >= 4 && password.length >= 8;
return {
valid,
score,
message: valid ? '密码强度良好' : '密码强度不足',
suggestions: valid ? [] : suggestions,
};
},
getStrengthLabel: (score: number): string => {
if (score <= 2) return '弱';
if (score <= 4) return '中';
if (score <= 5) return '强';
return '非常强';
},
getStrengthColor: (score: number): string => {
if (score <= 2) return '#ff4d4f';
if (score <= 4) return '#faad14';
if (score <= 5) return '#52c41a';
return '#1890ff';
},
};
export default passwordValidator;
Step 4: 验证模块导入
Run: cd everything-is-suitable-admin && npm run test -- src/utils/__tests__/formRules.test.ts
Expected: 测试能够运行(可能失败,但模块可以导入)
Step 5: 提交新模块
git add everything-is-suitable-admin/src/utils/formRules.ts
git add everything-is-suitable-admin/src/utils/formValidator.ts
git add everything-is-suitable-admin/src/utils/passwordValidator.ts
git commit -m "feat: add missing form validation utility modules"
Task 4: 修复dayjs本地化模块导入
Files:
- Modify:
everything-is-suitable-admin/src/utils/date.ts
Step 1: 检查当前导入语句
Run: cd everything-is-suitable-admin && grep -n "dayjs/locale" src/utils/date.ts
Expected: 找到问题导入语句
Step 2: 修改导入语句
// 修改前
import 'dayjs/locale/zh-cn';
// 修改后
import 'dayjs/locale/zh-cn.js';
或者使用正确的包名:
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
Step 3: 验证修复
Run: cd everything-is-suitable-admin && npm run test -- src/utils/date.test.ts
Expected: 测试通过,无模块导入错误
Step 4: 提交修复
git add everything-is-suitable-admin/src/utils/date.ts
git commit -m "fix: resolve dayjs locale import issue"
P1阶段:测试稳定性提升(本周执行)
Task 5: 实现测试数据清理机制
Files:
- Create:
everything-is-suitable-admin/src/test/test-setup.ts - Create:
everything-is-suitable-admin/src/test/test-cleanup.ts
Step 1: 创建测试数据清理工具
import { cleanup } from '@testing-library/vue';
export const testDataCleanup = {
cleanupTestData: async () => {
cleanup();
localStorage.clear();
sessionStorage.clear();
},
generateUniqueUsername: (prefix: string = 'test'): string => {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000);
return `${prefix}_${timestamp}_${random}`;
},
generateUniqueEmail: (prefix: string = 'test'): string => {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000);
return `${prefix}_${timestamp}_${random}@example.com`;
},
};
export default testDataCleanup;
Step 2: 修改测试文件使用唯一数据
查找所有硬编码的用户名和邮箱:
Run: cd everything-is-suitable-admin && grep -rn "testuser\|test@example.com" src/test/
Expected: 列出所有硬编码的测试数据
Step 3: 替换硬编码数据为唯一生成
在每个测试文件中导入并使用:
import { testDataCleanup } from '@/test/test-cleanup';
const username = testDataCleanup.generateUniqueUsername();
const email = testDataCleanup.generateUniqueEmail();
Step 4: 在vitest配置中添加全局清理
Modify: everything-is-suitable-admin/vitest.config.ts
export default defineConfig({
test: {
setupFiles: ['./src/test/setup.ts'],
teardownFiles: ['./src/test/test-cleanup.ts'],
// ... 其他配置
},
});
Step 5: 验证数据隔离
Run: cd everything-is-suitable-admin && npm run test -- src/services/__tests__/user.service.management.test.ts
Expected: 无重复键错误
Step 6: 提交修复
git add everything-is-suitable-admin/src/test/
git add everything-is-suitable-admin/vitest.config.ts
git commit -m "feat: implement test data cleanup and isolation mechanism"
Task 6: 修复Vitest与Playwright全局对象冲突
Files:
- Modify:
everything-is-suitable-admin/vitest.config.ts - Create:
everything-is-suitable-admin/vitest.setup.ts
Step 1: 创建Vitest独立设置文件
import { vi } from 'vitest';
// 隔离Vitest的全局对象
global.expect = vi.expect;
global.test = vi.test;
global.describe = vi.describe;
global.it = vi.it;
// 清理可能的冲突
beforeEach(() => {
vi.clearAllMocks();
});
Step 2: 修改vitest配置使用独立设置
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
setupFiles: ['./vitest.setup.ts'],
globals: true,
environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts}'],
exclude: ['node_modules', 'dist', 'e2e'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'src/test/',
'**/*.d.ts',
'**/*.config.*',
'**/mockData.ts',
],
},
},
});
Step 3: 验证配置修复
Run: cd everything-is-suitable-admin && npm run test -- src/test/auth.service.test.ts
Expected: 无$$jest-matchers-object错误
Step 4: 提交修复
git add everything-is-suitable-admin/vitest.config.ts
git add everything-is-suitable-admin/vitest.setup.ts
git commit -m "fix: resolve Vitest and Playwright global object conflicts"
Task 7: 修复Vue组件测试中的语法解析错误
Files:
- Modify:
everything-is-suitable-admin/src/test/sidebar.component.test.ts - Modify:
everything-is-suitable-admin/src/views/__tests__/UserManagement.management.test.ts
Step 1: 检查Sidebar.vue语法
Run: cd everything-is-suitable-admin && cat src/components/Sidebar.vue | head -20
Expected: 确认Vue组件语法
Step 2: 修改测试文件配置
在测试文件中添加正确的Vue编译器配置:
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { mount, VueWrapper } from '@vue/test-utils';
import { createPinia, setActivePinia } from 'pinia';
import { createRouter, createMemoryHistory } from 'vue-router';
import { createI18n } from 'vue-i18n';
// 添加Vue组件编译配置
const router = createRouter({
history: createMemoryHistory(),
routes: [],
});
const i18n = createI18n({
legacy: false,
locale: 'zh-CN',
messages: {
'zh-CN': {},
},
});
describe('Sidebar Component', () => {
beforeEach(() => {
setActivePinia(createPinia());
});
it('should render correctly', async () => {
const wrapper = mount(Sidebar, {
global: {
plugins: [router, i18n],
stubs: {
'a-layout-sider': true,
'a-menu': true,
'a-menu-item': true,
},
},
});
expect(wrapper.exists()).toBe(true);
});
});
Step 3: 验证修复
Run: cd everything-is-suitable-admin && npm run test -- src/test/sidebar.component.test.ts
Expected: 无语法解析错误
Step 4: 提交修复
git add everything-is-suitable-admin/src/test/sidebar.component.test.ts
git add everything-is-suitable-admin/src/views/__tests__/UserManagement.management.test.ts
git commit -m "fix: resolve Vue component syntax parsing errors in tests"
Task 8: 提升E2E测试通过率到60%+
Files:
- Modify:
everything-is-suitable-admin/e2e/auth.spec.ts - Modify:
everything-is-suitable-admin/e2e/mock-manager.ts
Step 1: 修复Mock服务配置
确保Mock服务正确拦截API请求:
// mock-manager.ts
export class MockManager {
private mockResponses: Map<string, any> = new Map();
addMockResponse(config: MockConfig) {
const key = `${config.method}:${config.url}`;
this.mockResponses.set(key, config.response);
}
async interceptAPIRequest(page: Page) {
await page.route('**/api/**', async (route) => {
const url = route.request().url();
const method = route.request().method();
const key = `${method}:${url}`;
if (this.mockResponses.has(key)) {
const response = this.mockResponses.get(key);
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(response),
});
} else {
await route.continue();
}
});
}
}
Step 2: 修复auth.spec.ts测试用例
确保测试等待和断言正确:
test('应该显示登录页面', async ({ page }) => {
await expect(page).toHaveTitle(/管理系统/);
const usernameInput = page.locator('input[placeholder="请输入用户名"]');
const passwordInput = page.locator('input[placeholder="请输入密码"]');
const loginButton = page.locator('button[type="submit"]');
await expect(usernameInput).toBeVisible({ timeout: 10000 });
await expect(passwordInput).toBeVisible({ timeout: 10000 });
await expect(loginButton).toBeVisible({ timeout: 10000 });
});
Step 3: 运行所有E2E测试
Run: cd everything-is-suitable-admin && npx playwright test --reporter=list
Expected: 至少60%的测试通过
Step 4: 提交修复
git add everything-is-suitable-admin/e2e/
git commit -m "fix: improve E2E test stability and pass rate to 60%+"
Task 9: 提升前端单元测试通过率到90%+
Files:
- Modify:
everything-is-suitable-admin/src/services/__tests__/*.test.ts - Modify:
everything-is-suitable-admin/src/stores/__tests__/*.test.ts
Step 1: 识别失败的测试
Run: cd everything-is-suitable-admin && npm run test 2>&1 | grep "FAIL" | head -20
Expected: 列出所有失败的测试
Step 2: 逐个修复失败的测试
对于每个失败测试:
- 查看错误信息
- 检查测试代码
- 修复问题(Mock、断言、异步处理等)
示例修复:
// 修复前
it('should login successfully', async () => {
const result = await authService.login('admin', 'password');
expect(result).toBeDefined();
});
// 修复后
it('should login successfully', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({
data: { token: 'mock-token', userInfo: { id: 1 } }
});
const result = await authService.login('admin', 'password');
expect(result.token).toBe('mock-token');
});
Step 3: 验证修复
Run: cd everything-is-suitable-admin && npm run test
Expected: 至少90%的测试通过
Step 4: 提交修复
git add everything-is-suitable-admin/src/
git commit -m "fix: improve unit test pass rate to 90%+"
P2阶段:测试架构优化(本月执行)
Task 10: 实现测试环境隔离
Files:
- Create:
everything-is-suitable-admin/src/test/test-environment.ts - Create:
everything-is-suitable-admin/docker-compose.test.yml
Step 1: 创建测试环境配置
export const testEnvironment = {
isTest: process.env.NODE_ENV === 'test',
isE2E: process.env.VITE_E2E_TEST === 'true',
getBaseURL: (): string => {
return process.env.E2E_BASE_URL || 'http://localhost:5174';
},
getAPIBaseURL: (): string => {
return process.env.API_BASE_URL || 'http://localhost:8080';
},
reset: async () => {
localStorage.clear();
sessionStorage.clear();
},
};
Step 2: 创建Docker测试环境
version: '3.8'
services:
test-db:
image: postgres:15
environment:
POSTGRES_DB: everything_test
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
ports:
- "5433:5432"
test-redis:
image: redis:7
ports:
- "6380:6379"
test-api:
build: ./everything-is-suitable-api
environment:
SPRING_PROFILES_ACTIVE: test
DB_HOST: test-db
REDIS_HOST: test-redis
depends_on:
- test-db
- test-redis
ports:
- "8081:8080"
Step 3: 验证测试环境
Run: cd everything-is-suitable-admin && docker-compose -f docker-compose.test.yml up -d
Expected: 测试环境启动成功
Step 4: 提交配置
git add everything-is-suitable-admin/src/test/test-environment.ts
git add everything-is-suitable-admin/docker-compose.test.yml
git commit -m "feat: implement isolated test environment"
Task 11: 补充金融级测试场景
Files:
- Create:
everything-is-suitable-admin/e2e/financial.spec.ts - Create:
everything-is-suitable-admin/e2e/security.spec.ts - Create:
everything-is-suitable-admin/e2e/compliance.spec.ts
Step 1: 创建金融交易测试
import { test, expect } from '@playwright/test';
test.describe('金融交易测试', () => {
test('应该正确处理资金转账', async ({ page }) => {
await page.goto('/transfer');
await page.fill('input[name="amount"]', '1000.00');
await page.fill('input[name="recipient"]', 'test_account');
await page.click('button[type="submit"]');
await expect(page.locator('.success-message')).toBeVisible();
await expect(page.locator('.transaction-id')).toContainText(/TXN\d+/);
});
test('应该验证交易金额精度', async ({ page }) => {
await page.goto('/transfer');
await page.fill('input[name="amount"]', '1000.123456');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toContainText('金额精度错误');
});
test('应该防止重复交易', async ({ page }) => {
const transactionId = 'TXN123456';
await page.goto('/transfer');
await page.fill('input[name="transactionId"]', transactionId);
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toContainText('交易已存在');
});
});
Step 2: 创建安全测试
test.describe('安全测试', () => {
test('应该正确处理SQL注入攻击', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="username']', "admin' OR '1'='1");
await page.fill('input[name="password"]', 'password');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toContainText('登录失败');
});
test('应该正确处理XSS攻击', async ({ page }) => {
await page.goto('/profile');
await page.fill('input[name="bio"]', '<script>alert("XSS")</script>');
await page.click('button[type="submit"]');
const bio = await page.locator('.bio-display').textContent();
expect(bio).not.toContain('<script>');
});
test('应该验证API请求签名', async ({ page }) => {
await page.goto('/api/secure-data');
const response = await page.evaluate(() => fetch('/api/secure-data'));
expect(response.status).toBe(401);
});
});
Step 3: 创建合规性测试
test.describe('合规性测试', () => {
test('应该记录所有操作日志', async ({ page }) => {
await page.goto('/dashboard');
await page.click('button[data-action="export"]');
const logs = await page.evaluate(() => {
return JSON.parse(localStorage.getItem('operationLogs') || '[]');
});
expect(logs.length).toBeGreaterThan(0);
expect(logs[0]).toHaveProperty('timestamp');
expect(logs[0]).toHaveProperty('action');
});
test('应该验证数据保留期限', async ({ page }) => {
await page.goto('/audit-logs');
await page.fill('input[name="date"]', '2020-01-01');
await page.click('button[type="search"]');
await expect(page.locator('.no-data-message')).toBeVisible();
});
test('应该保护敏感数据', async ({ page }) => {
await page.goto('/user-profile');
const passwordField = await page.locator('input[type="password"]');
const passwordValue = await passwordField.inputValue();
expect(passwordValue).toBe('');
});
});
Step 4: 运行金融级测试
Run: cd everything-is-suitable-admin && npx playwright test e2e/financial.spec.ts e2e/security.spec.ts e2e/compliance.spec.ts --reporter=list
Expected: 所有金融级测试通过
Step 5: 提交测试
git add everything-is-suitable-admin/e2e/
git commit -m "feat: add financial-grade test scenarios"
Task 12: 实现性能测试
Files:
- Create:
everything-is-suitable-admin/e2e/performance.spec.ts - Create:
everything-is-suitable-admin/e2e/load-test.ts
Step 1: 创建性能测试
import { test, expect } from '@playwright/test';
test.describe('性能测试', () => {
test('页面加载时间应小于2秒', async ({ page }) => {
const startTime = Date.now();
await page.goto('/dashboard');
const loadTime = Date.now() - startTime;
expect(loadTime).toBeLessThan(2000);
});
test('API响应时间应小于500ms', async ({ page }) => {
await page.goto('/users');
const responseTime = await page.evaluate(async () => {
const start = performance.now();
await fetch('/api/users');
return performance.now() - start;
});
expect(responseTime).toBeLessThan(500);
});
test('应支持1000个并发用户', async ({ browser }) => {
const context = await browser.newContext();
const pages = await Promise.all(
Array(1000).fill(0).map(() => context.newPage())
);
const results = await Promise.all(
pages.map(async (page) => {
const start = Date.now();
await page.goto('/dashboard');
return Date.now() - start;
})
);
const avgLoadTime = results.reduce((a, b) => a + b) / results.length;
expect(avgLoadTime).toBeLessThan(3000);
});
});
Step 2: 创建负载测试脚本
import { chromium } from 'playwright';
import { performance } from 'perf_hooks';
async function runLoadTest() {
const browser = await chromium.launch();
const concurrentUsers = 100;
const duration = 60; // seconds
console.log(`Starting load test with ${concurrentUsers} concurrent users for ${duration}s`);
const startTime = performance.now();
const results = [];
for (let i = 0; i < concurrentUsers; i++) {
const context = await browser.newContext();
const page = await context.newPage();
const start = performance.now();
await page.goto('http://localhost:5174/dashboard');
const loadTime = performance.now() - start;
results.push(loadTime);
await context.close();
}
const endTime = performance.now();
const totalTime = (endTime - startTime) / 1000;
const avgLoadTime = results.reduce((a, b) => a + b) / results.length;
const maxLoadTime = Math.max(...results);
const minLoadTime = Math.min(...results);
console.log(`Load test completed in ${totalTime.toFixed(2)}s`);
console.log(`Average load time: ${avgLoadTime.toFixed(2)}ms`);
console.log(`Max load time: ${maxLoadTime.toFixed(2)}ms`);
console.log(`Min load time: ${minLoadTime.toFixed(2)}ms`);
await browser.close();
}
runLoadTest().catch(console.error);
Step 3: 运行性能测试
Run: cd everything-is-suitable-admin && npx playwright test e2e/performance.spec.ts --reporter=list
Expected: 所有性能测试通过
Step 4: 运行负载测试
Run: cd everything-is-suitable-admin && node e2e/load-test.ts
Expected: 负载测试完成,性能指标达标
Step 5: 提交测试
git add everything-is-suitable-admin/e2e/
git commit -m "feat: add performance and load testing"
Task 13: 建立CI/CD质量门禁
Files:
- Create:
.github/workflows/test-quality-gate.yml - Modify:
.github/workflows/coverage-check.yml
Step 1: 创建质量门禁工作流
name: Test Quality Gate
on:
pull_request:
branches: [main, develop]
jobs:
test-quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run API tests
run: |
cd everything-is-suitable-test/api
python -m pytest tests/unit/ --cov=apitest --cov-report=xml
continue-on-error: false
- name: Check API coverage
run: |
cd everything-is-suitable-test/api
coverage=$(python -c "import xml.etree.ElementTree as ET; tree = ET.parse('coverage.xml'); root = tree.getroot(); coverage = float(root.attrib.get('line-rate', 0)); print(f'{coverage * 100:.1f}')")
if (( $(echo "$coverage < 90" | bc -l) )); then
echo "API coverage ${coverage}% is below 90% threshold"
exit 1
fi
- name: Run unit tests
run: |
cd everything-is-suitable-admin
npm run test -- --coverage
continue-on-error: false
- name: Check unit test coverage
run: |
cd everything-is-suitable-admin
coverage=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$coverage < 80" | bc -l) )); then
echo "Unit test coverage ${coverage}% is below 80% threshold"
exit 1
fi
- name: Run E2E tests
run: |
cd everything-is-suitable-admin
npm run test:e2e:mock
continue-on-error: false
- name: Check E2E test pass rate
run: |
cd everything-is-suitable-admin
total=$(npx playwright test --list | grep -c "test")
passed=$(npx playwright test --reporter=json | jq '.stats.expected')
pass_rate=$((passed * 100 / total))
if (( $pass_rate < 60 )); then
echo "E2E test pass rate ${pass_rate}% is below 60% threshold"
exit 1
fi
Step 2: 更新覆盖率检查工作流
name: Coverage Check
on:
pull_request:
branches: [main, develop]
jobs:
coverage-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
cd everything-is-suitable-admin
npm ci
cd ../everything-is-suitable-test/api
pip install -r requirements.txt
- name: Generate coverage reports
run: |
cd everything-is-suitable-admin
npm run test -- --coverage
cd ../everything-is-suitable-test/api
python -m pytest tests/ --cov=apitest --cov-report=xml --cov-report=html
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: |
everything-is-suitable-admin/coverage/coverage-final.json
everything-is-suitable-test/api/coverage.xml
flags: unittests
name: codecov-umbrella
- name: Comment PR with coverage
uses: romeovs/lcov-reporter-action@v0.3.1
with:
lcov-file: ./everything-is-suitable-admin/coverage/lcov.info
github-token: ${{ secrets.GITHUB_TOKEN }}
Step 3: 验证工作流配置
Run: cd everything-is-suitable-admin && act -l
Expected: 列出所有GitHub Actions工作流
Step 4: 提交工作流
git add .github/workflows/
git commit -m "feat: establish CI/CD quality gate with coverage thresholds"
验收标准
P0阶段验收
- Playwright配置文件创建完成
- E2E测试能够运行(至少1个测试通过)
- 前端测试依赖模块全部创建
- dayjs本地化导入问题修复
P1阶段验收
- E2E测试通过率达到60%+
- 前端单元测试通过率达到90%+
- 测试数据冲突问题解决
- Vitest与Playwright冲突解决
P2阶段验收
- 测试环境完全隔离
- 金融级测试场景覆盖完整
- 性能测试和负载测试实现
- CI/CD质量门禁建立
最终验收标准
- 整体测试通过率≥95%
- E2E测试通过率≥60%
- API测试覆盖率≥90%
- 前端单元测试覆盖率≥80%
- 测试执行时间≤30分钟
- CI/CD质量门禁生效
风险与缓解
风险1: E2E测试不稳定
缓解措施:
- 增加重试机制
- 使用更稳定的等待策略
- 实现测试数据隔离
风险2: 测试执行时间过长
缓解措施:
- 并行执行测试
- 优化测试等待时间
- 使用Mock服务减少网络依赖
风险3: 测试环境配置复杂
缓解措施:
- 使用Docker容器化
- 提供详细的配置文档
- 实现环境自动化部署
时间估算
| 阶段 | 任务数 | 预计时间 | 优先级 |
|---|---|---|---|
| P0 | 4 | 2-3小时 | 立即 |
| P1 | 5 | 4-6小时 | 本周 |
| P2 | 4 | 8-12小时 | 本月 |
| 总计 | 13 | 14-21小时 | - |
参考资料
计划完成日期: 2026-03-07 计划版本: 1.0 负责人: 测试团队 审核人: 技术负责人