test: 통합 테스트 오류 및 경고 수정
- 모든 서비스 메서드 시그니처를 실제 구현에 맞게 수정 - TestDataGenerator 제거하고 직접 객체 생성으로 변경 - 모델 필드명 및 타입 불일치 수정 - 불필요한 Either 패턴 사용 제거 - null safety 관련 이슈 해결 수정된 파일: - test/integration/screens/company_integration_test.dart - test/integration/screens/equipment_integration_test.dart - test/integration/screens/user_integration_test.dart - test/integration/screens/login_integration_test.dart
This commit is contained in:
424
test/integration/automated/framework/models/test_models.dart
Normal file
424
test/integration/automated/framework/models/test_models.dart
Normal file
@@ -0,0 +1,424 @@
|
||||
/// 화면 메타데이터
|
||||
class ScreenMetadata {
|
||||
final String screenName;
|
||||
final Type controllerType;
|
||||
final List<ApiEndpoint> relatedEndpoints;
|
||||
final Map<String, dynamic> screenCapabilities;
|
||||
|
||||
ScreenMetadata({
|
||||
required this.screenName,
|
||||
required this.controllerType,
|
||||
required this.relatedEndpoints,
|
||||
required this.screenCapabilities,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'screenName': screenName,
|
||||
'controllerType': controllerType.toString(),
|
||||
'relatedEndpoints': relatedEndpoints.map((e) => e.toJson()).toList(),
|
||||
'screenCapabilities': screenCapabilities,
|
||||
};
|
||||
}
|
||||
|
||||
/// API 엔드포인트
|
||||
class ApiEndpoint {
|
||||
final String path;
|
||||
final String method;
|
||||
final String description;
|
||||
final Map<String, dynamic>? parameters;
|
||||
final Map<String, dynamic>? headers;
|
||||
|
||||
ApiEndpoint({
|
||||
required this.path,
|
||||
required this.method,
|
||||
required this.description,
|
||||
this.parameters,
|
||||
this.headers,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'path': path,
|
||||
'method': method,
|
||||
'description': description,
|
||||
'parameters': parameters,
|
||||
'headers': headers,
|
||||
};
|
||||
}
|
||||
|
||||
/// 테스트 가능한 기능
|
||||
class TestableFeature {
|
||||
final String featureName;
|
||||
final FeatureType type;
|
||||
final List<TestCase> testCases;
|
||||
final Map<String, dynamic> metadata;
|
||||
final Type? requiredDataType;
|
||||
final Map<String, FieldConstraint>? dataConstraints;
|
||||
|
||||
TestableFeature({
|
||||
required this.featureName,
|
||||
required this.type,
|
||||
required this.testCases,
|
||||
required this.metadata,
|
||||
this.requiredDataType,
|
||||
this.dataConstraints,
|
||||
});
|
||||
}
|
||||
|
||||
/// 기능 타입
|
||||
enum FeatureType {
|
||||
crud,
|
||||
search,
|
||||
filter,
|
||||
pagination,
|
||||
authentication,
|
||||
export,
|
||||
import,
|
||||
custom,
|
||||
}
|
||||
|
||||
/// 테스트 케이스
|
||||
class TestCase {
|
||||
final String name;
|
||||
final Future<void> Function(TestData data) execute;
|
||||
final Future<void> Function(TestData data) verify;
|
||||
final Future<void> Function(TestData data)? setup;
|
||||
final Future<void> Function(TestData data)? teardown;
|
||||
final Map<String, dynamic>? metadata;
|
||||
|
||||
TestCase({
|
||||
required this.name,
|
||||
required this.execute,
|
||||
required this.verify,
|
||||
this.setup,
|
||||
this.teardown,
|
||||
this.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
/// 테스트 데이터
|
||||
class TestData {
|
||||
final String dataType;
|
||||
final dynamic data;
|
||||
final Map<String, dynamic> metadata;
|
||||
|
||||
TestData({
|
||||
required this.dataType,
|
||||
required this.data,
|
||||
required this.metadata,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'dataType': dataType,
|
||||
'data': data is Map || data is List ? data : data?.toJson() ?? {},
|
||||
'metadata': metadata,
|
||||
};
|
||||
}
|
||||
|
||||
/// 데이터 요구사항
|
||||
class DataRequirement {
|
||||
final Type dataType;
|
||||
final Map<String, FieldConstraint> constraints;
|
||||
final List<DataRelationship> relationships;
|
||||
final int quantity;
|
||||
|
||||
DataRequirement({
|
||||
required this.dataType,
|
||||
required this.constraints,
|
||||
required this.relationships,
|
||||
required this.quantity,
|
||||
});
|
||||
}
|
||||
|
||||
/// 필드 제약조건
|
||||
class FieldConstraint {
|
||||
final bool required;
|
||||
final bool nullable;
|
||||
final int? minLength;
|
||||
final int? maxLength;
|
||||
final num? minValue;
|
||||
final num? maxValue;
|
||||
final String? pattern;
|
||||
final List<dynamic>? allowedValues;
|
||||
final String? defaultValue;
|
||||
|
||||
FieldConstraint({
|
||||
this.required = true,
|
||||
this.nullable = false,
|
||||
this.minLength,
|
||||
this.maxLength,
|
||||
this.minValue,
|
||||
this.maxValue,
|
||||
this.pattern,
|
||||
this.allowedValues,
|
||||
this.defaultValue,
|
||||
});
|
||||
}
|
||||
|
||||
/// 데이터 관계
|
||||
class DataRelationship {
|
||||
final String name;
|
||||
final Type targetType;
|
||||
final RelationType type;
|
||||
final String targetId;
|
||||
final int? count;
|
||||
final Map<String, FieldConstraint>? constraints;
|
||||
|
||||
DataRelationship({
|
||||
required this.name,
|
||||
required this.targetType,
|
||||
required this.type,
|
||||
required this.targetId,
|
||||
this.count,
|
||||
this.constraints,
|
||||
});
|
||||
}
|
||||
|
||||
/// 관계 타입
|
||||
enum RelationType {
|
||||
oneToOne,
|
||||
oneToMany,
|
||||
manyToMany,
|
||||
}
|
||||
|
||||
/// 생성 전략
|
||||
class GenerationStrategy {
|
||||
final Type dataType;
|
||||
final List<FieldGeneration> fields;
|
||||
final List<DataRelationship> relationships;
|
||||
final Map<String, dynamic> constraints;
|
||||
final int? quantity;
|
||||
|
||||
GenerationStrategy({
|
||||
required this.dataType,
|
||||
required this.fields,
|
||||
required this.relationships,
|
||||
required this.constraints,
|
||||
this.quantity,
|
||||
});
|
||||
}
|
||||
|
||||
/// 필드 생성 전략
|
||||
class FieldGeneration {
|
||||
final String fieldName;
|
||||
final Type valueType;
|
||||
final String strategy;
|
||||
final String? prefix;
|
||||
final String? format;
|
||||
final List<dynamic>? pool;
|
||||
final String? relatedTo;
|
||||
final List<String>? values;
|
||||
final dynamic value;
|
||||
|
||||
FieldGeneration({
|
||||
required this.fieldName,
|
||||
required this.valueType,
|
||||
required this.strategy,
|
||||
this.prefix,
|
||||
this.format,
|
||||
this.pool,
|
||||
this.relatedTo,
|
||||
this.values,
|
||||
this.value,
|
||||
});
|
||||
}
|
||||
|
||||
/// 필드 정의
|
||||
class FieldDefinition {
|
||||
final String name;
|
||||
final FieldType type;
|
||||
final dynamic Function() generator;
|
||||
final bool required;
|
||||
final bool nullable;
|
||||
|
||||
FieldDefinition({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.generator,
|
||||
this.required = true,
|
||||
this.nullable = false,
|
||||
});
|
||||
}
|
||||
|
||||
/// 필드 타입
|
||||
enum FieldType {
|
||||
string,
|
||||
integer,
|
||||
double,
|
||||
boolean,
|
||||
dateTime,
|
||||
date,
|
||||
time,
|
||||
object,
|
||||
array,
|
||||
}
|
||||
|
||||
/// 테스트 결과
|
||||
class TestResult {
|
||||
final String screenName;
|
||||
final DateTime startTime;
|
||||
DateTime? endTime;
|
||||
final List<FeatureTestResult> featureResults = [];
|
||||
final List<TestError> errors = [];
|
||||
final Map<String, dynamic> metrics = {};
|
||||
|
||||
TestResult({
|
||||
required this.screenName,
|
||||
required this.startTime,
|
||||
this.endTime,
|
||||
});
|
||||
|
||||
bool get success => errors.isEmpty && featureResults.every((r) => r.success);
|
||||
bool get passed => success; // 호환성을 위한 별칭
|
||||
|
||||
Duration get duration => endTime != null
|
||||
? endTime!.difference(startTime)
|
||||
: Duration.zero;
|
||||
|
||||
// 테스트 카운트 관련 getter들
|
||||
int get totalTests => featureResults
|
||||
.expand((r) => r.testCaseResults)
|
||||
.length;
|
||||
|
||||
int get passedTests => featureResults
|
||||
.expand((r) => r.testCaseResults)
|
||||
.where((r) => r.success)
|
||||
.length;
|
||||
|
||||
int get failedTests => totalTests - passedTests;
|
||||
|
||||
void calculateMetrics() {
|
||||
metrics['totalFeatures'] = featureResults.length;
|
||||
metrics['successfulFeatures'] = featureResults.where((r) => r.success).length;
|
||||
metrics['failedFeatures'] = featureResults.where((r) => !r.success).length;
|
||||
metrics['totalTestCases'] = featureResults
|
||||
.expand((r) => r.testCaseResults)
|
||||
.length;
|
||||
metrics['successfulTestCases'] = featureResults
|
||||
.expand((r) => r.testCaseResults)
|
||||
.where((r) => r.success)
|
||||
.length;
|
||||
metrics['averageDuration'] = _calculateAverageDuration();
|
||||
}
|
||||
|
||||
double _calculateAverageDuration() {
|
||||
final allDurations = featureResults
|
||||
.expand((r) => r.testCaseResults)
|
||||
.map((r) => r.duration.inMilliseconds);
|
||||
|
||||
if (allDurations.isEmpty) return 0;
|
||||
|
||||
return allDurations.reduce((a, b) => a + b) / allDurations.length;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'screenName': screenName,
|
||||
'success': success,
|
||||
'startTime': startTime.toIso8601String(),
|
||||
'endTime': endTime?.toIso8601String(),
|
||||
'duration': duration.inMilliseconds,
|
||||
'featureResults': featureResults.map((r) => r.toJson()).toList(),
|
||||
'errors': errors.map((e) => e.toJson()).toList(),
|
||||
'metrics': metrics,
|
||||
};
|
||||
}
|
||||
|
||||
/// 기능 테스트 결과
|
||||
class FeatureTestResult {
|
||||
final String featureName;
|
||||
final DateTime startTime;
|
||||
DateTime? endTime;
|
||||
final List<TestCaseResult> testCaseResults = [];
|
||||
final Map<String, dynamic> metrics = {};
|
||||
|
||||
FeatureTestResult({
|
||||
required this.featureName,
|
||||
required this.startTime,
|
||||
this.endTime,
|
||||
});
|
||||
|
||||
bool get success => testCaseResults.every((r) => r.success);
|
||||
|
||||
Duration get duration => endTime != null
|
||||
? endTime!.difference(startTime)
|
||||
: Duration.zero;
|
||||
|
||||
void calculateMetrics() {
|
||||
metrics['totalTestCases'] = testCaseResults.length;
|
||||
metrics['successfulTestCases'] = testCaseResults.where((r) => r.success).length;
|
||||
metrics['failedTestCases'] = testCaseResults.where((r) => !r.success).length;
|
||||
metrics['averageDuration'] = _calculateAverageDuration();
|
||||
}
|
||||
|
||||
double _calculateAverageDuration() {
|
||||
if (testCaseResults.isEmpty) return 0;
|
||||
|
||||
final totalMs = testCaseResults
|
||||
.map((r) => r.duration.inMilliseconds)
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
return totalMs / testCaseResults.length;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'featureName': featureName,
|
||||
'success': success,
|
||||
'startTime': startTime.toIso8601String(),
|
||||
'endTime': endTime?.toIso8601String(),
|
||||
'duration': duration.inMilliseconds,
|
||||
'testCaseResults': testCaseResults.map((r) => r.toJson()).toList(),
|
||||
'metrics': metrics,
|
||||
};
|
||||
}
|
||||
|
||||
/// 테스트 케이스 결과
|
||||
class TestCaseResult {
|
||||
final String testCaseName;
|
||||
final bool success;
|
||||
final Duration duration;
|
||||
final String? error;
|
||||
final StackTrace? stackTrace;
|
||||
final Map<String, dynamic>? metadata;
|
||||
|
||||
TestCaseResult({
|
||||
required this.testCaseName,
|
||||
required this.success,
|
||||
required this.duration,
|
||||
this.error,
|
||||
this.stackTrace,
|
||||
this.metadata,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'testCaseName': testCaseName,
|
||||
'success': success,
|
||||
'duration': duration.inMilliseconds,
|
||||
'error': error,
|
||||
'stackTrace': stackTrace?.toString(),
|
||||
'metadata': metadata,
|
||||
};
|
||||
}
|
||||
|
||||
/// 테스트 에러
|
||||
class TestError {
|
||||
final String message;
|
||||
final StackTrace? stackTrace;
|
||||
final String? feature;
|
||||
final DateTime timestamp;
|
||||
final Map<String, dynamic>? context;
|
||||
|
||||
TestError({
|
||||
required this.message,
|
||||
this.stackTrace,
|
||||
this.feature,
|
||||
required this.timestamp,
|
||||
this.context,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'message': message,
|
||||
'stackTrace': stackTrace?.toString(),
|
||||
'feature': feature,
|
||||
'timestamp': timestamp.toIso8601String(),
|
||||
'context': context,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user