From 6c6e9f002f058c85896b5fd54a6a3607798bc4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Fri, 13 Mar 2026 11:58:15 +0800 Subject: [PATCH] feat: add test performance optimization tool --- e2e/src/utils/test-optimizer.ts | 222 ++++++++++++++++++++++++++++++ e2e/test-optimizer-simple-test.js | 170 +++++++++++++++++++++++ 2 files changed, 392 insertions(+) create mode 100644 e2e/src/utils/test-optimizer.ts create mode 100644 e2e/test-optimizer-simple-test.js diff --git a/e2e/src/utils/test-optimizer.ts b/e2e/src/utils/test-optimizer.ts new file mode 100644 index 0000000..4f2b09c --- /dev/null +++ b/e2e/src/utils/test-optimizer.ts @@ -0,0 +1,222 @@ +interface TestPerformance { + testId: string; + file: string; + title: string; + duration: number; + tier: string; + avgDuration: number; + percentile: number; + isSlow: boolean; + optimizationSuggestions: string[]; +} + +interface OptimizationRule { + name: string; + condition: (perf: TestPerformance) => boolean; + suggestions: string[]; +} + +export class TestOptimizer { + private rules: OptimizationRule[] = []; + + constructor() { + this.initializeRules(); + } + + private initializeRules(): void { + this.rules = [ + { + name: 'slow-test', + condition: (p) => p.duration > 60000, + suggestions: [ + '考虑将测试拆分为多个小测试', + '检查是否有不必要的等待时间', + '优化选择器以提高定位速度', + '考虑使用mock数据替代真实API调用', + ], + }, + { + name: 'very-slow-test', + condition: (p) => p.duration > 120000, + suggestions: [ + '测试执行时间过长,强烈建议拆分', + '检查是否有性能瓶颈(如大量DOM操作)', + '考虑使用并行测试策略', + '评估是否需要完整加载所有资源', + ], + }, + { + name: 'flaky-test', + condition: (p) => p.percentile > 90, + suggestions: [ + '测试执行时间波动较大,可能存在稳定性问题', + '增加重试次数或使用更稳定的等待策略', + '检查网络请求的稳定性', + '考虑添加更明确的断言条件', + ], + }, + { + name: 'tier-fast-slow', + condition: (p) => p.tier === 'fast' && p.duration > 30000, + suggestions: [ + '快速层测试不应超过30秒', + '重新评估测试是否属于快速层', + '优化测试逻辑或移至标准层', + ], + }, + { + name: 'tier-standard-slow', + condition: (p) => p.tier === 'standard' && p.duration > 90000, + suggestions: [ + '标准层测试不应超过90秒', + '考虑拆分测试或优化执行流程', + '评估是否需要移至深度层', + ], + }, + ]; + } + + analyzePerformance( + testId: string, + file: string, + title: string, + duration: number, + tier: string, + history: number[] + ): TestPerformance { + const avgDuration = history.length > 0 + ? history.reduce((sum, d) => sum + d, 0) / history.length + : duration; + + const sortedHistory = [...history].sort((a, b) => a - b); + const percentile = sortedHistory.length > 0 + ? sortedHistory[Math.floor(sortedHistory.length * 0.9)] + : duration; + + const isSlow = duration > 60000; + + const suggestions: string[] = []; + for (const rule of this.rules) { + if (rule.condition({ + testId, + file, + title, + duration, + tier, + avgDuration, + percentile, + isSlow, + optimizationSuggestions: [], + })) { + suggestions.push(...rule.suggestions); + } + } + + return { + testId, + file, + title, + duration, + tier, + avgDuration, + percentile, + isSlow, + optimizationSuggestions: suggestions, + }; + } + + optimizeTestSuite(performances: TestPerformance[]): { + totalTests: number; + slowTests: number; + potentialSavings: number; + recommendations: string[]; + } { + const slowTests = performances.filter(p => p.isSlow); + const totalDuration = performances.reduce((sum, p) => sum + p.duration, 0); + const slowDuration = slowTests.reduce((sum, p) => sum + p.duration, 0); + + const potentialSavings = slowDuration * 0.3; // 假设优化可节省30%时间 + + const recommendations: string[] = []; + + if (slowTests.length > 0) { + recommendations.push(`发现 ${slowTests.length} 个慢速测试,建议优先优化`); + } + + const tierSlowTests = { + fast: slowTests.filter(p => p.tier === 'fast').length, + standard: slowTests.filter(p => p.tier === 'standard').length, + deep: slowTests.filter(p => p.tier === 'deep').length, + }; + + if (tierSlowTests.fast > 0) { + recommendations.push(`${tierSlowTests.fast} 个快速层测试执行过慢,建议重新评估分层`); + } + + if (tierSlowTests.standard > 5) { + recommendations.push(`${tierSlowTests.standard} 个标准层测试执行过慢,建议优化或拆分`); + } + + const avgDuration = totalDuration / performances.length; + if (avgDuration > 60000) { + recommendations.push('平均测试执行时间超过60秒,建议整体优化测试策略'); + } + + return { + totalTests: performances.length, + slowTests: slowTests.length, + potentialSavings, + recommendations, + }; + } + + generateOptimizationReport(performances: TestPerformance[]): string { + const analysis = this.optimizeTestSuite(performances); + + let report = '🚀 测试性能优化报告\n'; + report += '='.repeat(50) + '\n\n'; + + report += `总测试数: ${analysis.totalTests}\n`; + report += `慢速测试: ${analysis.slowTests}\n`; + report += `潜在节省时间: ${(analysis.potentialSavings / 1000).toFixed(2)}s\n\n`; + + if (analysis.recommendations.length > 0) { + report += '📋 优化建议:\n'; + analysis.recommendations.forEach((rec, index) => { + report += ` ${index + 1}. ${rec}\n`; + }); + report += '\n'; + } + + const slowTests = performances.filter(p => p.isSlow) + .sort((a, b) => b.duration - a.duration); + + if (slowTests.length > 0) { + report += '🐌 慢速测试详情:\n'; + slowTests.forEach((test, index) => { + report += `\n${index + 1}. ${test.title}\n`; + report += ` 文件: ${test.file}\n`; + report += ` 耗时: ${(test.duration / 1000).toFixed(2)}s\n`; + report += ` 层级: ${test.tier}\n`; + if (test.optimizationSuggestions.length > 0) { + report += ` 优化建议:\n`; + test.optimizationSuggestions.forEach(suggestion => { + report += ` - ${suggestion}\n`; + }); + } + }); + } else { + report += '✅ 未发现慢速测试\n'; + } + + return report; + } + + addRule(rule: OptimizationRule): void { + this.rules.push(rule); + } + + removeRule(ruleName: string): void { + this.rules = this.rules.filter(r => r.name !== ruleName); + } +} \ No newline at end of file diff --git a/e2e/test-optimizer-simple-test.js b/e2e/test-optimizer-simple-test.js new file mode 100644 index 0000000..a0425eb --- /dev/null +++ b/e2e/test-optimizer-simple-test.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const path = require('path'); + +console.log('🚀 测试性能优化工具...'); + +const mockTestResults = [ + { + testId: 'test-1', + file: 'smoke/navigation.smoke.spec.ts', + title: '应该成功加载首页', + duration: 15000, + tier: 'fast', + history: [12000, 14000, 15000, 16000, 18000], + }, + { + testId: 'test-2', + file: 'admin/news-management.spec.ts', + title: '应该能够创建新闻', + duration: 45000, + tier: 'standard', + history: [40000, 42000, 45000, 48000, 50000], + }, + { + testId: 'test-3', + file: 'api/admin.api.spec.ts', + title: '应该能够获取内容列表', + duration: 80000, + tier: 'fast', + history: [60000, 70000, 80000, 90000, 100000], + }, + { + testId: 'test-4', + file: 'visual/homepage-visual.spec.ts', + title: '首页视觉回归测试', + duration: 150000, + tier: 'deep', + history: [120000, 130000, 150000, 170000, 180000], + }, + { + testId: 'test-5', + file: 'responsive/mobile-navigation.spec.ts', + title: '移动端导航功能测试', + duration: 95000, + tier: 'standard', + history: [80000, 85000, 95000, 105000, 110000], + }, +]; + +const optimizer = { + rules: [ + { + name: 'slow-test', + condition: (p) => p.duration > 60000, + suggestions: [ + '考虑将测试拆分为多个小测试', + '检查是否有不必要的等待时间', + '优化选择器以提高定位速度', + ], + }, + { + name: 'very-slow-test', + condition: (p) => p.duration > 120000, + suggestions: [ + '测试执行时间过长,强烈建议拆分', + '检查是否有性能瓶颈(如大量DOM操作)', + ], + }, + { + name: 'tier-fast-slow', + condition: (p) => p.tier === 'fast' && p.duration > 30000, + suggestions: [ + '快速层测试不应超过30秒', + '重新评估测试是否属于快速层', + ], + }, + ], + + analyzePerformance(testId, file, title, duration, tier, history) { + const avgDuration = history.length > 0 + ? history.reduce((sum, d) => sum + d, 0) / history.length + : duration; + + const sortedHistory = [...history].sort((a, b) => a - b); + const percentile = sortedHistory.length > 0 + ? sortedHistory[Math.floor(sortedHistory.length * 0.9)] + : duration; + + const isSlow = duration > 60000; + + const suggestions = []; + for (const rule of this.rules) { + if (rule.condition({ testId, file, title, duration, tier, avgDuration, percentile, isSlow })) { + suggestions.push(...rule.suggestions); + } + } + + return { + testId, + file, + title, + duration, + tier, + avgDuration, + percentile, + isSlow, + optimizationSuggestions: suggestions, + }; + }, + + generateOptimizationReport(performances) { + const slowTests = performances.filter(p => p.isSlow); + const totalDuration = performances.reduce((sum, p) => sum + p.duration, 0); + const slowDuration = slowTests.reduce((sum, p) => sum + p.duration, 0); + const potentialSavings = slowDuration * 0.3; + + let report = '🚀 测试性能优化报告\n'; + report += '='.repeat(50) + '\n\n'; + + report += `总测试数: ${performances.length}\n`; + report += `慢速测试: ${slowTests.length}\n`; + report += `潜在节省时间: ${(potentialSavings / 1000).toFixed(2)}s\n\n`; + + const sortedSlowTests = slowTests.sort((a, b) => b.duration - a.duration); + + if (sortedSlowTests.length > 0) { + report += '🐌 慢速测试详情:\n'; + sortedSlowTests.forEach((test, index) => { + report += `\n${index + 1}. ${test.title}\n`; + report += ` 文件: ${test.file}\n`; + report += ` 耗时: ${(test.duration / 1000).toFixed(2)}s\n`; + report += ` 层级: ${test.tier}\n`; + if (test.optimizationSuggestions.length > 0) { + report += ` 优化建议:\n`; + test.optimizationSuggestions.forEach(suggestion => { + report += ` - ${suggestion}\n`; + }); + } + }); + } else { + report += '✅ 未发现慢速测试\n'; + } + + return report; + }, +}; + +console.log('📊 分析测试性能...'); + +const performances = mockTestResults.map(result => + optimizer.analyzePerformance( + result.testId, + result.file, + result.title, + result.duration, + result.tier, + result.history + ) +); + +const report = optimizer.generateOptimizationReport(performances); +console.log(report); + +const slowTestsCount = performances.filter(p => p.isSlow).length; +if (slowTestsCount > 0) { + console.log(`\n✅ 优化器工作正常,发现 ${slowTestsCount} 个慢速测试`); + process.exit(0); +} else { + console.log('\n⚠️ 未发现慢速测试'); + process.exit(1); +} \ No newline at end of file