test: 통합 테스트 오류 및 경고 수정
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled

- 모든 서비스 메서드 시그니처를 실제 구현에 맞게 수정
- 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:
JiWoong Sul
2025-08-05 20:24:05 +09:00
parent d6f34c0a52
commit 198aac6525
145 changed files with 41527 additions and 5220 deletions

View File

@@ -0,0 +1,269 @@
import 'package:dio/dio.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/datasources/remote/auth_remote_datasource.dart';
import 'package:superport/data/datasources/remote/company_remote_datasource.dart';
import 'package:superport/data/datasources/remote/user_remote_datasource.dart';
import 'package:superport/data/datasources/remote/equipment_remote_datasource.dart';
import 'package:superport/data/datasources/remote/license_remote_datasource.dart';
import 'package:superport/data/datasources/remote/warehouse_remote_datasource.dart';
import 'package:superport/data/datasources/remote/dashboard_remote_datasource.dart';
import 'package:superport/data/models/auth/login_request.dart';
import 'package:superport/services/auth_service.dart';
import 'package:superport/services/company_service.dart';
import 'package:superport/services/user_service.dart';
import 'package:superport/services/equipment_service.dart';
import 'package:superport/services/license_service.dart';
import 'package:superport/services/warehouse_service.dart';
import 'package:superport/services/dashboard_service.dart';
import 'package:superport/core/config/environment.dart';
/// 테스트용 메모리 기반 FlutterSecureStorage
class TestSecureStorage extends FlutterSecureStorage {
static final Map<String, String> _storage = {};
const TestSecureStorage() : super();
@override
Future<bool> containsKey({required String key, IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
return _storage.containsKey(key);
}
@override
Future<void> delete({required String key, IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
_storage.remove(key);
}
@override
Future<void> deleteAll({IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
_storage.clear();
}
@override
Future<String?> read({required String key, IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
return _storage[key];
}
@override
Future<Map<String, String>> readAll({IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
return Map.from(_storage);
}
@override
Future<void> write({required String key, required String? value, IOSOptions? iOptions, AndroidOptions? aOptions, LinuxOptions? lOptions, WebOptions? webOptions, MacOsOptions? mOptions, WindowsOptions? wOptions}) async {
if (value != null) {
_storage[key] = value;
} else {
_storage.remove(key);
}
}
// 테스트용 메서드
static void clearAll() {
_storage.clear();
}
}
/// 실제 API 테스트를 위한 헬퍼 클래스
class RealApiTestHelper {
static late GetIt getIt;
static late ApiClient apiClient;
static late FlutterSecureStorage secureStorage;
static late AuthService authService;
static String? _accessToken;
/// 테스트 환경 초기화
static Future<void> setupTestEnvironment() async {
// Environment 초기화
await Environment.initialize('development');
// 테스트 환경에서는 TestWidgetsFlutterBinding을 사용하지 않음
// HTTP 요청이 차단되기 때문
getIt = GetIt.instance;
// GetIt 초기화
if (getIt.isRegistered<ApiClient>()) {
await getIt.reset();
}
// 실제 API 클라이언트 설정
apiClient = ApiClient();
secureStorage = const TestSecureStorage();
// 서비스 등록
getIt.registerSingleton<ApiClient>(apiClient);
getIt.registerSingleton<FlutterSecureStorage>(secureStorage);
// Auth 서비스 등록
final authRemoteDataSource = AuthRemoteDataSourceImpl(apiClient);
authService = AuthServiceImpl(authRemoteDataSource, secureStorage);
getIt.registerSingleton<AuthService>(authService);
// RemoteDataSource 등록 (일부 서비스가 GetIt을 통해 가져옴)
final companyRemoteDataSource = CompanyRemoteDataSourceImpl(apiClient);
final licenseRemoteDataSource = LicenseRemoteDataSourceImpl(apiClient: apiClient);
final warehouseRemoteDataSource = WarehouseRemoteDataSourceImpl(apiClient: apiClient);
final equipmentRemoteDataSource = EquipmentRemoteDataSourceImpl();
final userRemoteDataSource = UserRemoteDataSource();
final dashboardRemoteDataSource = DashboardRemoteDataSourceImpl(apiClient);
getIt.registerSingleton<CompanyRemoteDataSource>(companyRemoteDataSource);
getIt.registerSingleton<LicenseRemoteDataSource>(licenseRemoteDataSource);
getIt.registerSingleton<WarehouseRemoteDataSource>(warehouseRemoteDataSource);
getIt.registerSingleton<EquipmentRemoteDataSource>(equipmentRemoteDataSource);
getIt.registerSingleton<UserRemoteDataSource>(userRemoteDataSource);
getIt.registerSingleton<DashboardRemoteDataSource>(dashboardRemoteDataSource);
// 기타 서비스 등록
getIt.registerSingleton<CompanyService>(CompanyService(companyRemoteDataSource));
getIt.registerSingleton<UserService>(UserService());
getIt.registerSingleton<EquipmentService>(EquipmentService());
getIt.registerSingleton<LicenseService>(LicenseService(licenseRemoteDataSource));
getIt.registerSingleton<WarehouseService>(WarehouseService());
getIt.registerSingleton<DashboardService>(DashboardServiceImpl(dashboardRemoteDataSource));
}
/// 로그인 수행 및 토큰 저장
static Future<String> loginAndGetToken() async {
if (_accessToken != null) {
return _accessToken!;
}
final loginRequest = LoginRequest(
email: 'admin@superport.kr',
password: 'admin123!',
);
final result = await authService.login(loginRequest);
return result.fold(
(failure) => throw Exception('로그인 실패: ${failure.message}'),
(loginResponse) {
_accessToken = loginResponse.accessToken;
apiClient.updateAuthToken(_accessToken!);
return _accessToken!;
},
);
}
/// 테스트 환경 정리
static Future<void> teardownTestEnvironment() async {
_accessToken = null;
apiClient.removeAuthToken();
// 테스트용 스토리지 정리
TestSecureStorage.clearAll();
await getIt.reset();
}
/// API 응답 로깅 헬퍼
static void logResponse(String testName, Response response) {
// === $testName ===
// Status Code: ${response.statusCode}
// Headers: ${response.headers}
// Data: ${response.data}
// =================
}
/// 에러 로깅 헬퍼
static void logError(String testName, dynamic error) {
// === $testName - ERROR ===
if (error is DioException) {
// Type: ${error.type}
// Message: ${error.message}
// Response: ${error.response?.data}
// Status Code: ${error.response?.statusCode}
} else {
// Error: $error
}
// ========================
}
}
/// 테스트 데이터 생성 헬퍼
class TestDataHelper {
static int _uniqueId = DateTime.now().millisecondsSinceEpoch;
static String generateUniqueId() {
return '${_uniqueId++}';
}
static String generateUniqueEmail() {
return 'test_${generateUniqueId()}@test.com';
}
static String generateUniqueName(String prefix) {
return '${prefix}_${generateUniqueId()}';
}
/// 테스트용 회사 데이터
static Map<String, dynamic> createTestCompanyData() {
return {
'name': generateUniqueName('Test Company'),
'business_number': '123-45-${generateUniqueId().substring(0, 5)}',
'phone': '010-${_uniqueId % 10000}-${(_uniqueId + 1) % 10000}',
'address': {
'zip_code': '12345',
'region': '서울시 강남구',
'detail_address': '테스트로 ${_uniqueId % 100}번길',
},
};
}
/// 테스트용 사용자 데이터
static Map<String, dynamic> createTestUserData({required int companyId}) {
return {
'email': generateUniqueEmail(),
'password': 'Test1234!',
'name': generateUniqueName('Test User'),
'phone': '010-${_uniqueId % 10000}-${(_uniqueId + 1) % 10000}',
'company_id': companyId,
'role': 'M', // Member
'is_active': true,
};
}
/// 테스트용 장비 데이터
static Map<String, dynamic> createTestEquipmentData({
required int companyId,
required int warehouseId,
}) {
return {
'name': generateUniqueName('Test Equipment'),
'model': 'Model-${generateUniqueId()}',
'serial_number': 'SN-${generateUniqueId()}',
'company_id': companyId,
'warehouse_id': warehouseId,
'status': 'I', // 입고
'quantity': 1,
'purchase_date': DateTime.now().toIso8601String(),
};
}
/// 테스트용 라이선스 데이터
static Map<String, dynamic> createTestLicenseData({required int companyId}) {
return {
'name': generateUniqueName('Test License'),
'product_key': 'KEY-${generateUniqueId()}',
'company_id': companyId,
'license_type': 'subscription',
'quantity': 5,
'expiry_date': DateTime.now().add(const Duration(days: 365)).toIso8601String(),
'purchase_date': DateTime.now().toIso8601String(),
};
}
/// 테스트용 창고 데이터
static Map<String, dynamic> createTestWarehouseData({required int companyId}) {
return {
'name': generateUniqueName('Test Warehouse'),
'company_id': companyId,
'location': '서울시 강남구 테스트로 ${_uniqueId % 100}',
'capacity': 1000,
'manager': generateUniqueName('Manager'),
'contact': '02-${_uniqueId % 10000}-${(_uniqueId + 1) % 10000}',
};
}
}