- test/integration/automated만 유지하고 나머지 테스트 삭제 - 삭제: api/, helpers/, unit/, widget/, fixtures/ 폴더 - 삭제: mock, 개별 통합 테스트 파일들 - 유지: automated 테스트 (실제 API + 자동화 시나리오) - 테스트 오류 수정 - debugPrint 함수 정의 오류 해결 (foundation import 추가) - ApiAutoFixer diagnostics 파라미터 누락 수정 - 타입 불일치 오류 수정 - 최종 상태 - 자동화 테스트 40개 파일 유지 - 오류 337개 → 2개 warning으로 감소 (99.4% 해결) - 실제 API 연동 테스트 정상 작동 확인
367 lines
11 KiB
Dart
367 lines
11 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:superport/services/equipment_service.dart';
|
|
import 'package:superport/models/equipment_unified_model.dart';
|
|
import 'base_screen_test.dart';
|
|
import '../../framework/models/test_models.dart';
|
|
|
|
/// BaseScreenTest를 상속받아 구현하는 예제
|
|
///
|
|
/// 이 예제는 Equipment 화면 테스트를 구현하는 방법을 보여줍니다.
|
|
/// 다른 화면들도 이와 유사한 방식으로 구현할 수 있습니다.
|
|
class ExampleEquipmentScreenTest extends BaseScreenTest {
|
|
late EquipmentService equipmentService;
|
|
|
|
ExampleEquipmentScreenTest({
|
|
required super.apiClient,
|
|
required super.getIt,
|
|
required super.testContext,
|
|
required super.errorDiagnostics,
|
|
required super.autoFixer,
|
|
required super.dataGenerator,
|
|
required super.reportCollector,
|
|
});
|
|
|
|
// ===== 필수 구현 메서드들 =====
|
|
|
|
@override
|
|
ScreenMetadata getScreenMetadata() {
|
|
return ScreenMetadata(
|
|
screenName: 'EquipmentListScreen',
|
|
controllerType: EquipmentService,
|
|
relatedEndpoints: [
|
|
ApiEndpoint(
|
|
path: '/api/v1/equipment',
|
|
method: 'GET',
|
|
description: '장비 목록 조회',
|
|
),
|
|
ApiEndpoint(
|
|
path: '/api/v1/equipment',
|
|
method: 'POST',
|
|
description: '장비 생성',
|
|
),
|
|
ApiEndpoint(
|
|
path: '/api/v1/equipment/{id}',
|
|
method: 'PUT',
|
|
description: '장비 수정',
|
|
),
|
|
ApiEndpoint(
|
|
path: '/api/v1/equipment/{id}',
|
|
method: 'DELETE',
|
|
description: '장비 삭제',
|
|
),
|
|
],
|
|
screenCapabilities: {
|
|
'crud': {
|
|
'create': true,
|
|
'read': true,
|
|
'update': true,
|
|
'delete': true,
|
|
},
|
|
'search': {
|
|
'enabled': true,
|
|
'fields': ['name', 'serialNumber', 'manufacturer'],
|
|
},
|
|
'filter': {
|
|
'enabled': true,
|
|
'fields': ['status', 'category', 'location'],
|
|
},
|
|
'pagination': {
|
|
'enabled': true,
|
|
'defaultPerPage': 20,
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> initializeServices() async {
|
|
equipmentService = getIt<EquipmentService>();
|
|
}
|
|
|
|
@override
|
|
dynamic getService() => equipmentService;
|
|
|
|
@override
|
|
String getResourceType() => 'equipment';
|
|
|
|
@override
|
|
Map<String, dynamic> getDefaultFilters() {
|
|
return {
|
|
'status': 'active',
|
|
'category': 'all',
|
|
};
|
|
}
|
|
|
|
// ===== CRUD 작업 구현 =====
|
|
|
|
@override
|
|
Future<dynamic> performCreateOperation(TestData data) async {
|
|
// TestData에서 Equipment 객체로 변환
|
|
final equipmentData = data.data;
|
|
final equipment = Equipment(
|
|
manufacturer: equipmentData['manufacturer'] ?? 'Unknown',
|
|
name: equipmentData['name'] ?? 'Test Equipment',
|
|
category: equipmentData['category'] ?? '미분류',
|
|
subCategory: equipmentData['subCategory'] ?? '',
|
|
subSubCategory: equipmentData['subSubCategory'] ?? '',
|
|
serialNumber: equipmentData['serialNumber'] ?? 'SN-${DateTime.now().millisecondsSinceEpoch}',
|
|
quantity: equipmentData['quantity'] ?? 1,
|
|
inDate: equipmentData['inDate'] ?? DateTime.now().toIso8601String(),
|
|
remark: equipmentData['remark'],
|
|
);
|
|
|
|
return await equipmentService.createEquipment(equipment);
|
|
}
|
|
|
|
@override
|
|
Future<dynamic> performReadOperation(TestData data) async {
|
|
// 페이지네이션 파라미터 사용
|
|
final page = data.data['page'] ?? 1;
|
|
final perPage = data.data['perPage'] ?? 20;
|
|
|
|
return await equipmentService.getEquipments(
|
|
page: page,
|
|
perPage: perPage,
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<dynamic> performUpdateOperation(dynamic resourceId, Map<String, dynamic> updateData) async {
|
|
// 기존 장비 조회
|
|
final existing = await equipmentService.getEquipment(resourceId);
|
|
|
|
// 업데이트할 Equipment 객체 생성
|
|
final updated = Equipment(
|
|
id: existing.id,
|
|
manufacturer: updateData['manufacturer'] ?? existing.manufacturer,
|
|
name: updateData['name'] ?? existing.name,
|
|
category: updateData['category'] ?? existing.category,
|
|
subCategory: updateData['subCategory'] ?? existing.subCategory,
|
|
subSubCategory: updateData['subSubCategory'] ?? existing.subSubCategory,
|
|
serialNumber: updateData['serialNumber'] ?? existing.serialNumber,
|
|
quantity: updateData['quantity'] ?? existing.quantity,
|
|
inDate: updateData['inDate'] ?? existing.inDate,
|
|
remark: updateData['remark'] ?? existing.remark,
|
|
);
|
|
|
|
return await equipmentService.updateEquipment(resourceId, updated);
|
|
}
|
|
|
|
@override
|
|
Future<void> performDeleteOperation(dynamic resourceId) async {
|
|
await equipmentService.deleteEquipment(resourceId);
|
|
}
|
|
|
|
@override
|
|
dynamic extractResourceId(dynamic resource) {
|
|
if (resource is Equipment) {
|
|
return resource.id;
|
|
}
|
|
return resource['id'] ?? resource.id;
|
|
}
|
|
|
|
// ===== 선택적 구현 메서드들 (필요시 오버라이드) =====
|
|
|
|
@override
|
|
Future<void> validateDataBeforeCreate(TestData data) async {
|
|
// 장비 생성 전 데이터 검증
|
|
final equipmentData = data.data;
|
|
|
|
// 필수 필드 검증
|
|
if (equipmentData['manufacturer'] == null || equipmentData['manufacturer'].isEmpty) {
|
|
throw ValidationError('제조사는 필수 입력 항목입니다');
|
|
}
|
|
|
|
if (equipmentData['name'] == null || equipmentData['name'].isEmpty) {
|
|
throw ValidationError('장비명은 필수 입력 항목입니다');
|
|
}
|
|
|
|
// 시리얼 번호 형식 검증
|
|
final serialNumber = equipmentData['serialNumber'];
|
|
if (serialNumber != null && !RegExp(r'^[A-Z0-9\-]+$').hasMatch(serialNumber)) {
|
|
throw ValidationError('시리얼 번호는 영문 대문자, 숫자, 하이픈만 사용 가능합니다');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<Map<String, dynamic>> prepareUpdateData(TestData data, dynamic resourceId) async {
|
|
// 기본 구현에 추가로 장비별 특수 로직 적용
|
|
final updateData = await super.prepareUpdateData(data, resourceId);
|
|
|
|
// 장비 상태 업데이트 시 이력 추가
|
|
if (updateData.containsKey('status')) {
|
|
updateData['statusChangeReason'] = '테스트 상태 변경';
|
|
updateData['statusChangedAt'] = DateTime.now().toIso8601String();
|
|
}
|
|
|
|
return updateData;
|
|
}
|
|
|
|
@override
|
|
Future<void> performAdditionalSetup() async {
|
|
// 장비 테스트를 위한 추가 설정
|
|
_log('장비 테스트용 카테고리 마스터 데이터 확인');
|
|
|
|
// 필요한 경우 카테고리 마스터 데이터 생성
|
|
// await _ensureCategoryMasterData();
|
|
}
|
|
|
|
@override
|
|
Future<void> performAdditionalCleanup() async {
|
|
// 장비 테스트 후 추가 정리
|
|
_log('장비 관련 임시 파일 정리');
|
|
|
|
// 테스트 중 생성된 임시 파일이나 캐시 정리
|
|
// await _cleanupTempFiles();
|
|
}
|
|
|
|
// ===== 커스텀 기능 테스트 =====
|
|
|
|
@override
|
|
Future<List<TestableFeature>> detectCustomFeatures(ScreenMetadata metadata) async {
|
|
final features = <TestableFeature>[];
|
|
|
|
// 장비 입출고 기능 테스트
|
|
features.add(TestableFeature(
|
|
featureName: 'Equipment In/Out',
|
|
type: FeatureType.custom,
|
|
testCases: [
|
|
TestCase(
|
|
name: 'Equipment check-in',
|
|
execute: (data) async {
|
|
await performEquipmentCheckIn(data);
|
|
},
|
|
verify: (data) async {
|
|
await verifyEquipmentCheckIn(data);
|
|
},
|
|
),
|
|
TestCase(
|
|
name: 'Equipment check-out',
|
|
execute: (data) async {
|
|
await performEquipmentCheckOut(data);
|
|
},
|
|
verify: (data) async {
|
|
await verifyEquipmentCheckOut(data);
|
|
},
|
|
),
|
|
],
|
|
metadata: {
|
|
'description': '장비 입출고 프로세스 테스트',
|
|
},
|
|
));
|
|
|
|
// 장비 이력 조회 기능 테스트
|
|
features.add(TestableFeature(
|
|
featureName: 'Equipment History',
|
|
type: FeatureType.custom,
|
|
testCases: [
|
|
TestCase(
|
|
name: 'View equipment history',
|
|
execute: (data) async {
|
|
await performViewHistory(data);
|
|
},
|
|
verify: (data) async {
|
|
await verifyViewHistory(data);
|
|
},
|
|
),
|
|
],
|
|
metadata: {
|
|
'description': '장비 이력 조회 테스트',
|
|
},
|
|
));
|
|
|
|
return features;
|
|
}
|
|
|
|
// 장비 입고 테스트
|
|
Future<void> performEquipmentCheckIn(TestData data) async {
|
|
// 먼저 장비 생성
|
|
await performCreate(data);
|
|
final equipmentId = testContext.getData('lastCreatedId');
|
|
|
|
// 입고 처리
|
|
final checkInResult = await equipmentService.equipmentIn(
|
|
equipmentId: equipmentId,
|
|
quantity: 1,
|
|
warehouseLocationId: testContext.getData('testWarehouseId') ?? 1,
|
|
notes: '테스트 입고',
|
|
);
|
|
|
|
testContext.setData('checkInResult', checkInResult);
|
|
}
|
|
|
|
Future<void> verifyEquipmentCheckIn(TestData data) async {
|
|
final checkInResult = testContext.getData('checkInResult');
|
|
expect(checkInResult, isNotNull, reason: '장비 입고 실패');
|
|
expect(checkInResult.success, isTrue, reason: '입고 처리가 성공하지 못했습니다');
|
|
}
|
|
|
|
// 장비 출고 테스트
|
|
Future<void> performEquipmentCheckOut(TestData data) async {
|
|
// 입고된 장비가 있는지 확인
|
|
final equipmentId = testContext.getData('lastCreatedId');
|
|
if (equipmentId == null) {
|
|
await performEquipmentCheckIn(data);
|
|
}
|
|
|
|
// 출고 처리
|
|
final checkOutResult = await equipmentService.equipmentOut(
|
|
equipmentId: equipmentId,
|
|
quantity: 1,
|
|
companyId: testContext.getData('testCompanyId') ?? 1,
|
|
notes: '테스트 출고',
|
|
);
|
|
|
|
testContext.setData('checkOutResult', checkOutResult);
|
|
}
|
|
|
|
Future<void> verifyEquipmentCheckOut(TestData data) async {
|
|
final checkOutResult = testContext.getData('checkOutResult');
|
|
expect(checkOutResult, isNotNull, reason: '장비 출고 실패');
|
|
expect(checkOutResult.success, isTrue, reason: '출고 처리가 성공하지 못했습니다');
|
|
}
|
|
|
|
// 장비 이력 조회 테스트
|
|
Future<void> performViewHistory(TestData data) async {
|
|
final equipmentId = testContext.getData('lastCreatedId');
|
|
if (equipmentId == null) {
|
|
await performCreate(data);
|
|
}
|
|
|
|
final history = await equipmentService.getEquipmentHistory(equipmentId);
|
|
testContext.setData('equipmentHistory', history);
|
|
}
|
|
|
|
Future<void> verifyViewHistory(TestData data) async {
|
|
final history = testContext.getData('equipmentHistory');
|
|
expect(history, isNotNull, reason: '장비 이력 조회 실패');
|
|
expect(history, isA<List>(), reason: '이력이 리스트 형식이 아닙니다');
|
|
}
|
|
|
|
// 로깅을 위한 헬퍼 메서드
|
|
void _log(String message) {
|
|
debugPrint('[ExampleEquipmentScreenTest] $message');
|
|
}
|
|
}
|
|
|
|
/// 검증 오류
|
|
class ValidationError implements Exception {
|
|
final String message;
|
|
|
|
ValidationError(this.message);
|
|
|
|
@override
|
|
String toString() => 'ValidationError: $message';
|
|
}
|
|
|
|
// 테스트 실행 예제
|
|
void main() {
|
|
group('Example Equipment Screen Test', () {
|
|
test('BaseScreenTest를 상속받아 구현하는 방법 예제', () {
|
|
// 이것은 예제 구현입니다.
|
|
// 실제 테스트는 프레임워크를 통해 실행됩니다.
|
|
expect(true, isTrue);
|
|
});
|
|
});
|
|
} |