/// 테스트 실행 결과를 담는 클래스 class TestResult { final String name; final int totalTests; final int passedTests; final int failedTests; final List failedTestNames; final Duration executionTime; final Map metadata; TestResult({ required this.name, required this.totalTests, required this.passedTests, required this.failedTests, this.failedTestNames = const [], required this.executionTime, this.metadata = const {}, }); double get passRate => totalTests > 0 ? (passedTests / totalTests) * 100 : 0; bool get isSuccess => failedTests == 0; String get summary { final emoji = isSuccess ? '✅' : '❌'; return '$emoji $name: $passedTests/$totalTests 통과 (${passRate.toStringAsFixed(1)}%)'; } Map toJson() => { 'name': name, 'totalTests': totalTests, 'passedTests': passedTests, 'failedTests': failedTests, 'failedTestNames': failedTestNames, 'executionTimeMs': executionTime.inMilliseconds, 'passRate': passRate, 'metadata': metadata, }; } /// 전체 테스트 스위트 결과 class TestSuiteResult { final List results; final DateTime timestamp; TestSuiteResult({ required this.results, DateTime? timestamp, }) : timestamp = timestamp ?? DateTime.now(); int get totalTests => results.fold(0, (sum, r) => sum + r.totalTests); int get passedTests => results.fold(0, (sum, r) => sum + r.passedTests); int get failedTests => results.fold(0, (sum, r) => sum + r.failedTests); double get overallPassRate => totalTests > 0 ? (passedTests / totalTests) * 100 : 0; bool get isSuccess => failedTests == 0; Duration get totalExecutionTime => Duration( milliseconds: results.fold(0, (sum, r) => sum + r.executionTime.inMilliseconds), ); String get summary { final buffer = StringBuffer(); buffer.writeln('\n${'=' * 60}'); buffer.writeln('📊 테스트 실행 결과 요약'); buffer.writeln('=' * 60); buffer.writeln('실행 시간: ${timestamp.toLocal()}'); buffer.writeln('총 실행 시간: ${totalExecutionTime.inSeconds}초'); buffer.writeln(''); for (final result in results) { buffer.writeln(result.summary); } buffer.writeln(''); buffer.writeln('-' * 60); buffer.writeln('전체 결과: $passedTests/$totalTests 통과 (${overallPassRate.toStringAsFixed(1)}%)'); if (isSuccess) { buffer.writeln('🎉 모든 테스트가 성공적으로 통과했습니다!'); } else { buffer.writeln('⚠️ 실패한 테스트가 있습니다.'); buffer.writeln('\n실패한 테스트 목록:'); for (final result in results) { if (result.failedTestNames.isNotEmpty) { buffer.writeln('\n${result.name}:'); for (final testName in result.failedTestNames) { buffer.writeln(' - $testName'); } } } } buffer.writeln('=' * 60); return buffer.toString(); } Map toJson() => { 'timestamp': timestamp.toIso8601String(), 'totalTests': totalTests, 'passedTests': passedTests, 'failedTests': failedTests, 'overallPassRate': overallPassRate, 'totalExecutionTimeMs': totalExecutionTime.inMilliseconds, 'results': results.map((r) => r.toJson()).toList(), }; }