import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:lanebow/config/api_config.dart'; import 'package:lanebow/models/club_model.dart'; import 'package:lanebow/services/api_service.dart'; import 'package:lanebow/services/club_service.dart'; import 'package:lanebow/services/event_bus.dart'; import 'package:lanebow/utils/test_utils.dart'; import 'club_service_integration_test.mocks.dart'; @GenerateMocks([ApiService]) void main() { late ClubService clubService; late MockApiService mockApiService; late String testId; setUp(() { // 테스트 ID 생성 및 설정 testId = 'club_service_integration_test_${DateTime.now().millisecondsSinceEpoch}'; EventBus.setCurrentTestId(testId); TestUtils.setTestMode(true); TestWidgetsFlutterBinding.ensureInitialized(); SharedPreferences.setMockInitialValues({ ApiConfig.clubIdKey: 'test_club_id' // SharedPreferences에 clubId 설정 }); mockApiService = MockApiService(); // 모든 테스트에서 공통으로 사용되는 모킹 설정 when(mockApiService.post( '/club', data: {'clubId': 'test_club_id'}, )).thenAnswer((_) async => { 'id': 'test_club_id', 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', }); clubService = ClubService.forTest(mockApiService); print('EventBus: 초기화됨 (테스트 ID: $testId)'); }); tearDown(() { // 테스트 종료 후 정리 clubService.dispose(); EventBus.clearCurrentTestId(); TestUtils.setTestMode(false); print('EventBus: 초기화됨 (테스트 ID: $testId)'); }); group('ClubService 통합 테스트', () { test('초기화 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // API 응답 모킹 when(mockApiService.setToken(token)).thenReturn(null); when(mockApiService.post( '${ApiConfig.clubs}', data: {'clubId': clubId}, )).thenAnswer((_) async => { 'id': clubId, 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', 'isActive': true, }); // when await clubService.initialize(token); // then expect(clubService.currentClub, isNotNull); expect(clubService.currentClub?.id, clubId); expect(clubService.currentClub?.name, '테스트 클럽'); verify(mockApiService.setToken(token)).called(1); // initialize에서 이미 fetchClubById를 호출하기 때문에 호출 횟수 검증을 제거합니다. }); test('사용자 클럽 목록 조회 테스트', () async { // given const token = 'test_token'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // API 응답 모킹 when(mockApiService.post('${ApiConfig.clubs}/user')).thenAnswer((_) async => [ { 'id': 'club_id_1', 'name': '테스트 클럽 1', 'description': '테스트 클럽 1 설명', 'ownerId': 'owner_id', }, { 'id': 'club_id_2', 'name': '테스트 클럽 2', 'description': '테스트 클럽 2 설명', 'ownerId': 'owner_id', } ]); // when await clubService.fetchUserClubs(); // then expect(clubService.clubs.length, 2); expect(clubService.clubs[0].id, 'club_id_1'); expect(clubService.clubs[0].name, '테스트 클럽 1'); expect(clubService.clubs[1].id, 'club_id_2'); expect(clubService.clubs[1].name, '테스트 클럽 2'); }); test('ID로 클럽 정보 조회 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // API 응답 모킹 when(mockApiService.post( '${ApiConfig.clubs}', data: {'clubId': clubId}, )).thenAnswer((_) async => { 'id': clubId, 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', 'isActive': true, }); // when await clubService.fetchClubById(clubId); // then expect(clubService.currentClub, isNotNull); expect(clubService.currentClub?.id, clubId); expect(clubService.currentClub?.name, '테스트 클럽'); }); test('현재 클럽 설정 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // API 응답 모킹 when(mockApiService.post( '${ApiConfig.clubs}', data: {'clubId': clubId}, )).thenAnswer((_) async => { 'id': clubId, 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', 'isActive': true, }); // when await clubService.setCurrentClub(clubId); // then expect(clubService.currentClub, isNotNull); expect(clubService.currentClub?.id, clubId); expect(clubService.currentClub?.name, '테스트 클럽'); }); test('클럽 선택 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // API 응답 모킹 when(mockApiService.post( '${ApiConfig.clubs}/select-club', data: {'clubId': clubId}, )).thenAnswer((_) async => {'success': true}); when(mockApiService.post( '${ApiConfig.clubs}', data: {'clubId': clubId}, )).thenAnswer((_) async => { 'id': clubId, 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', 'isActive': true, }); // EventBus 이벤트 리스너 설정 bool eventFired = false; final completer = Completer(); EventBus().on().listen((event) { eventFired = true; expect(event.clubId, clubId); completer.complete(); }); // when await clubService.selectClub(clubId); // 이벤트가 발생할 때까지 짧게 대기 await Future.any([completer.future, Future.delayed(const Duration(seconds: 1))]); // then expect(clubService.currentClub, isNotNull); expect(clubService.currentClub?.id, clubId); expect(clubService.currentClub?.name, '테스트 클럽'); expect(eventFired, true); }); test('클럽 생성 테스트', () async { // given const token = 'test_token'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); final newClub = Club( id: '', name: '새 클럽', description: '새 클럽 설명', ownerId: 'owner_id', ); // API 응답 모킹 when(mockApiService.post( '${ApiConfig.clubs}', data: anyNamed('data'), )).thenAnswer((_) async => { 'club': { 'id': 'new_club_id', 'name': '새 클럽', 'description': '새 클럽 설명', 'ownerId': 'owner_id', } }); // when final result = await clubService.createClub(newClub); // then expect(result.id, 'new_club_id'); expect(result.name, '새 클럽'); expect(clubService.currentClub?.id, 'new_club_id'); expect(clubService.clubs.length, 1); expect(clubService.clubs[0].id, 'new_club_id'); }); test('클럽 정보 업데이트 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // 클럽 목록 설정 when(mockApiService.post('${ApiConfig.clubs}/user')).thenAnswer((_) async => [ { 'id': clubId, 'name': '테스트 클럽', 'description': '테스트 클럽 설명', 'ownerId': 'owner_id', } ]); await clubService.fetchUserClubs(); final updates = { 'name': '수정된 클럽', 'description': '수정된 클럽 설명', }; // API 응답 모킹 when(mockApiService.put( ApiConfig.clubs, data: { 'clubId': clubId, ...updates, }, )).thenAnswer((_) async => { 'club': { 'id': clubId, 'name': '수정된 클럽', 'description': '수정된 클럽 설명', 'ownerId': 'owner_id', } }); // when final result = await clubService.updateClub(clubId, updates); // then expect(result.id, clubId); expect(result.name, '수정된 클럽'); expect(result.description, '수정된 클럽 설명'); expect(clubService.clubs[0].name, '수정된 클럽'); verify(mockApiService.put( ApiConfig.clubs, data: { 'clubId': clubId, ...updates, }, )).called(1); }); }); group('에러 처리 테스트', () { test('토큰 미설정 테스트', () async { // given // 토큰을 설정하지 않음 // when & then expect( () => clubService.fetchUserClubs(), returnsNormally, // 토큰이 없으면 조용히 반환됨 ); }); test('API 에러 처리 테스트', () async { // given const token = 'test_token'; const clubId = 'test_club_id'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); // API 에러 모킹 when(mockApiService.post( '${ApiConfig.clubs}', data: {'clubId': clubId}, )).thenThrow(Exception('API 에러 발생')); // when & then expect( () => clubService.fetchClubById(clubId), throwsA(isA()), ); }); test('클럽 생성 실패 테스트', () async { // given const token = 'test_token'; // 토큰 설정 when(mockApiService.setToken(token)).thenReturn(null); await clubService.initialize(token); final newClub = Club( id: '', name: '새 클럽', description: '새 클럽 설명', ownerId: 'owner_id', ); // API 에러 모킹 when(mockApiService.post( '${ApiConfig.clubs}', data: anyNamed('data'), )).thenThrow(Exception('클럽 생성 실패')); // when & then expect( () => clubService.createClub(newClub), throwsA(isA()), ); }); }); }