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:
269
test/integration/real_api/test_helper.dart
Normal file
269
test/integration/real_api/test_helper.dart
Normal 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}',
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user