feat: 자동화 테스트 프레임워크 Real API 전용으로 재구성
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

- Mock 서비스 제거 및 Real API 전용 테스트 헬퍼 추가
- Company, User, Warehouse 화면 테스트 클래스 신규 작성
- Master Test Suite에 모든 화면 테스트 통합
- 테스트 실행 스크립트 추가 (run_all_tests.sh)
- Clean Architecture 패턴 지원 준비

Note: UseCase 및 Repository 구현 후 완전한 테스트 실행 가능

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
JiWoong Sul
2025-08-11 14:09:59 +09:00
parent 1e6da44917
commit d64aa26157
6 changed files with 2111 additions and 11 deletions

View File

@@ -0,0 +1,339 @@
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import 'package:superport/data/datasources/remote/api_client.dart';
import 'package:superport/data/datasources/remote/api_interceptor.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/equipment_remote_datasource.dart';
import 'package:superport/data/datasources/remote/license_remote_datasource.dart';
import 'package:superport/data/datasources/remote/user_remote_datasource.dart';
import 'package:superport/data/datasources/remote/warehouse_location_remote_datasource.dart';
import 'package:superport/data/repositories/auth_repository_impl.dart';
import 'package:superport/data/repositories/company_repository_impl.dart';
import 'package:superport/data/repositories/equipment_repository_impl.dart';
import 'package:superport/data/repositories/license_repository_impl.dart';
import 'package:superport/data/repositories/user_repository_impl.dart';
import 'package:superport/data/repositories/warehouse_location_repository_impl.dart';
import 'package:superport/domain/repositories/auth_repository.dart';
import 'package:superport/domain/repositories/company_repository.dart';
import 'package:superport/domain/repositories/equipment_repository.dart';
import 'package:superport/domain/repositories/license_repository.dart';
import 'package:superport/domain/repositories/user_repository.dart';
import 'package:superport/domain/repositories/warehouse_location_repository.dart';
import 'package:superport/domain/usecases/auth/login_usecase.dart';
import 'package:superport/domain/usecases/company/create_company_usecase.dart';
import 'package:superport/domain/usecases/company/delete_company_usecase.dart';
import 'package:superport/domain/usecases/company/get_companies_usecase.dart';
import 'package:superport/domain/usecases/company/get_company_usecase.dart';
import 'package:superport/domain/usecases/company/toggle_company_status_usecase.dart';
import 'package:superport/domain/usecases/company/update_company_usecase.dart';
import 'package:superport/domain/usecases/equipment/create_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/delete_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_in_usecase.dart';
import 'package:superport/domain/usecases/equipment/equipment_out_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipment_usecase.dart';
import 'package:superport/domain/usecases/equipment/get_equipments_usecase.dart';
import 'package:superport/domain/usecases/equipment/update_equipment_usecase.dart';
import 'package:superport/domain/usecases/license/create_license_usecase.dart';
import 'package:superport/domain/usecases/license/delete_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_license_usecase.dart';
import 'package:superport/domain/usecases/license/get_licenses_usecase.dart';
import 'package:superport/domain/usecases/license/update_license_usecase.dart';
import 'package:superport/domain/usecases/user/create_user_usecase.dart';
import 'package:superport/domain/usecases/user/delete_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_user_usecase.dart';
import 'package:superport/domain/usecases/user/get_users_usecase.dart';
import 'package:superport/domain/usecases/user/update_user_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/create_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/delete_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_location_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/get_warehouse_locations_usecase.dart';
import 'package:superport/domain/usecases/warehouse_location/update_warehouse_location_usecase.dart';
import 'package:superport/services/auth_service.dart';
import 'package:superport/services/company_service.dart';
import 'package:superport/services/equipment_service.dart';
import 'package:superport/services/license_service.dart';
import 'package:superport/services/user_service.dart';
import 'package:superport/services/warehouse_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Real API 테스트 헬퍼
///
/// 실제 API 서버를 사용하는 테스트를 위한 의존성 주입 설정
class RealApiTestHelper {
static const String baseUrl = 'http://43.201.34.104:8080/api/v1';
static const String testEmail = 'admin@superport.kr';
static const String testPassword = 'admin123!';
static GetIt? _testGetIt;
static String? _accessToken;
/// 테스트 환경 설정
static Future<GetIt> setupTestEnvironment() async {
// 이미 설정되어 있으면 반환
if (_testGetIt != null) {
return _testGetIt!;
}
// GetIt 인스턴스 생성
_testGetIt = GetIt.instance;
// 기존 등록 정리
if (_testGetIt!.isRegistered<ApiClient>()) {
await _testGetIt!.reset();
}
// Dio 설정
final dio = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
// API 인터셉터 추가
dio.interceptors.add(ApiInterceptor());
// 로깅 인터셉터 추가 (디버그용)
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
error: true,
));
// SharedPreferences mock
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
// 의존성 등록 - 순서 중요!
// 1. 기본 인프라
_testGetIt!.registerSingleton<Dio>(dio);
_testGetIt!.registerSingleton<SharedPreferences>(prefs);
// 2. API Client
_testGetIt!.registerSingleton<ApiClient>(
ApiClient(dio),
);
// 3. Remote DataSources
_testGetIt!.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSource(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<CompanyRemoteDataSource>(
() => CompanyRemoteDataSource(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<EquipmentRemoteDataSource>(
() => EquipmentRemoteDataSource(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<LicenseRemoteDataSource>(
() => LicenseRemoteDataSource(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<UserRemoteDataSource>(
() => UserRemoteDataSource(_testGetIt!<ApiClient>()),
);
_testGetIt!.registerLazySingleton<WarehouseLocationRemoteDataSource>(
() => WarehouseLocationRemoteDataSource(_testGetIt!<ApiClient>()),
);
// 4. Repositories
_testGetIt!.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: _testGetIt!<AuthRemoteDataSource>(),
prefs: _testGetIt!<SharedPreferences>(),
),
);
_testGetIt!.registerLazySingleton<CompanyRepository>(
() => CompanyRepositoryImpl(
remoteDataSource: _testGetIt!<CompanyRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<EquipmentRepository>(
() => EquipmentRepositoryImpl(
remoteDataSource: _testGetIt!<EquipmentRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<LicenseRepository>(
() => LicenseRepositoryImpl(
remoteDataSource: _testGetIt!<LicenseRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(
remoteDataSource: _testGetIt!<UserRemoteDataSource>(),
),
);
_testGetIt!.registerLazySingleton<WarehouseLocationRepository>(
() => WarehouseLocationRepositoryImpl(
remoteDataSource: _testGetIt!<WarehouseLocationRemoteDataSource>(),
),
);
// 5. UseCases - Auth
_testGetIt!.registerLazySingleton(
() => LoginUseCase(repository: _testGetIt!<AuthRepository>()),
);
// 6. UseCases - Company
_testGetIt!.registerLazySingleton(
() => GetCompaniesUseCase(repository: _testGetIt!<CompanyRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteCompanyUseCase(repository: _testGetIt!<CompanyRepository>()),
);
_testGetIt!.registerLazySingleton(
() => ToggleCompanyStatusUseCase(repository: _testGetIt!<CompanyRepository>()),
);
// 7. UseCases - Equipment
_testGetIt!.registerLazySingleton(
() => GetEquipmentsUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteEquipmentUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentInUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
_testGetIt!.registerLazySingleton(
() => EquipmentOutUseCase(repository: _testGetIt!<EquipmentRepository>()),
);
// 8. UseCases - License
_testGetIt!.registerLazySingleton(
() => GetLicensesUseCase(repository: _testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteLicenseUseCase(repository: _testGetIt!<LicenseRepository>()),
);
// 9. UseCases - User
_testGetIt!.registerLazySingleton(
() => GetUsersUseCase(repository: _testGetIt!<UserRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetUserUseCase(repository: _testGetIt!<UserRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateUserUseCase(repository: _testGetIt!<UserRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateUserUseCase(repository: _testGetIt!<UserRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteUserUseCase(repository: _testGetIt!<UserRepository>()),
);
// 10. UseCases - Warehouse Location
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationsUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => GetWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => CreateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => UpdateWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
);
_testGetIt!.registerLazySingleton(
() => DeleteWarehouseLocationUseCase(repository: _testGetIt!<WarehouseLocationRepository>()),
);
// 11. Services (Legacy - 점진적 마이그레이션을 위해 유지)
_testGetIt!.registerLazySingleton<AuthService>(
() => AuthService(),
);
_testGetIt!.registerLazySingleton<CompanyService>(
() => CompanyService(),
);
_testGetIt!.registerLazySingleton<EquipmentService>(
() => EquipmentService(),
);
_testGetIt!.registerLazySingleton<LicenseService>(
() => LicenseService(),
);
_testGetIt!.registerLazySingleton<UserService>(
() => UserService(),
);
_testGetIt!.registerLazySingleton<WarehouseService>(
() => WarehouseService(),
);
return _testGetIt!;
}
/// 테스트용 로그인
static Future<String> loginForTest() async {
if (_accessToken != null) {
return _accessToken!;
}
final getIt = await setupTestEnvironment();
final loginUseCase = getIt<LoginUseCase>();
final result = await loginUseCase.execute(
email: testEmail,
password: testPassword,
);
return result.fold(
(failure) => throw Exception('테스트 로그인 실패: ${failure.message}'),
(loginResponse) {
_accessToken = loginResponse.accessToken;
// Dio 헤더에 토큰 설정
final dio = getIt<Dio>();
dio.options.headers['Authorization'] = 'Bearer $_accessToken';
return _accessToken!;
},
);
}
/// 테스트 환경 정리
static Future<void> teardownTestEnvironment() async {
_accessToken = null;
if (_testGetIt != null) {
await _testGetIt!.reset();
_testGetIt = null;
}
}
/// 테스트 데이터 정리 (선택적)
static Future<void> cleanupTestData() async {
// 테스트 중 생성된 데이터를 정리하는 로직
// 필요에 따라 구현
}
}