import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:mockito/annotations.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:lanebow/services/member_service.dart'; import 'package:lanebow/services/api_service.dart'; import 'package:lanebow/services/event_bus.dart'; // EventBus().fire() 호출에 필요 import 'package:lanebow/config/api_config.dart'; // 모킹 클래스 생성 @GenerateMocks([ApiService]) import 'member_service_test.mocks.dart'; void main() { late MemberService memberService; late MockApiService mockApiService; final testToken = 'test_token'; final testClubId = 'test_club_id'; final testMemberId = 'test_member_id'; // 테스트 데이터 초기화 final testMember = { 'id': 'test_member_id', 'name': '테스트 회원', 'email': 'test@example.com', 'phone': '010-1234-5678', 'gender': '남성', 'birthYear': 1990, 'level': '중급', 'position': '회원', 'joinDate': DateTime.now().toIso8601String(), 'clubId': 'test_club_id', 'isActive': true, }; setUp(() async { // SharedPreferences 모킹 설정 - 모든 테스트에서 사용 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: 'initial_club_id', }); // API 서비스 모킹 mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // 기본 API 호출 모킹 설정 - 모든 테스트에서 필요한 fetchClubMembers 설정 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 초기 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 모든 테스트에서 필요 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'initial_club_id'}, )).thenAnswer((_) async => [testMember]); // 새 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 이벤트 테스트에서 필요 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 초기화 (테스트용 생성자 사용) memberService = MemberService.forTest(mockApiService); // 토큰 설정 memberService.initialize(testToken); // 클럽 ID 설정 - 각 테스트에서 필요한 경우 직접 호출하도록 변경 // await memberService.setClubId(testClubId); // 모든 호출 기록 초기화 - 각 테스트가 독립적으로 실행되도록 함 clearInteractions(mockApiService); }); group('MemberService 테스트', () { test('initialize 메서드가 토큰을 설정해야 함', () async { // given final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: 'initial_club_id', }); // 초기 클럽 ID에 대한 fetchClubMembers 호출 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'initial_club_id'}, )).thenAnswer((_) async => [testMember]); // 새 클럽 ID에 대한 fetchClubMembers 호출 모킹 - 이벤트 테스트에서 필요 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // when final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 이벤트 처리를 위한 지연 - 더 긴 시간으로 설정 await Future.delayed(Duration(milliseconds: 500)); // then verify(mockApiService.setToken(testToken)).called(1); }); test('setClubId 메서드가 클럽 ID를 설정하고 회원 목록을 가져와야 함', () async { // given when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // when await memberService.setClubId(testClubId); // then verify(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).called(1); }); test('fetchClubMembers 메서드가 클럽의 회원 목록을 가져와야 함', () async { // given // 클럽 ID 설정 - 필수 await memberService.setClubId(testClubId); // 초기화 후 모든 호출 기록 초기화 clearInteractions(mockApiService); when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // when await memberService.fetchClubMembers(); // then verify(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).called(1); expect(memberService.members.length, 1); expect(memberService.members[0].id, testMemberId); expect(memberService.members[0].name, '테스트 회원'); }); test('fetchClubMembers \uba54\uc11c\ub4dc\uac00 \uac1d\uccb4 \ud615\ud0dc\uc758 \uc751\ub2f5\ub3c4 \ucc98\ub9ac\ud574\uc57c \ud568', () async { // given // \ud074\ub7fd ID \uc124\uc815 - \ud544\uc218 await memberService.setClubId(testClubId); // 초기화 후 모든 호출 기록 초기화 clearInteractions(mockApiService); when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => {'members': [testMember]}); // when await memberService.fetchClubMembers(); // then verify(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).called(1); expect(memberService.members.length, 1); expect(memberService.members[0].id, testMemberId); }); test('fetchMemberById 메서드가 특정 회원 정보를 가져와야 함', () async { // given // 클럽 ID 설정 - 필수 await memberService.setClubId(testClubId); when(mockApiService.post( '${ApiConfig.clubs}/members/detail', data: {'memberId': testMemberId}, )).thenAnswer((_) async => { 'statusCode': 200, 'member': testMember, }); // when final result = await memberService.fetchMemberById(testMemberId); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/detail', data: {'memberId': testMemberId}, )).called(1); expect(result.id, testMemberId); expect(result.name, '테스트 회원'); }); test('addMember 메서드가 새 회원을 추가해야 함', () async { // given // API 서비스 모킹 생성 final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: testClubId, }); // 클럽 회원 목록 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 이벤트 발생 시 new_club_id에 대한 모킹 추가 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 클럽 ID 설정 await memberService.setClubId(testClubId); // 초기화 후 모든 호출 기록 초기화 clearInteractions(mockApiService); final newMemberData = { 'name': '새 회원', 'email': 'new@example.com', 'phone': '010-9876-5432', 'clubId': testClubId, }; when(mockApiService.post( '${ApiConfig.clubs}/members/add', data: newMemberData, )).thenAnswer((_) async => {'member': testMember}); // when final result = await memberService.addMember(newMemberData); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/add', data: newMemberData, )).called(1); expect(result.id, testMemberId); }); test('addMember 메서드가 직접 회원 객체가 반환되는 경우도 처리해야 함', () async { // given // API 서비스 모킹 생성 final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: testClubId, }); // 클럽 회원 목록 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 이벤트 발생 시 new_club_id에 대한 모킹 추가 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 클럽 ID 설정 await memberService.setClubId(testClubId); // 초기화 후 모든 호출 기록 초기화 clearInteractions(mockApiService); final newMemberData = { 'name': '새 회원', 'email': 'new@example.com', 'phone': '010-9876-5432', 'clubId': testClubId, }; // 직접 회원 객체 반환 시ミュレイション when(mockApiService.post( '${ApiConfig.clubs}/members/add', data: newMemberData, )).thenAnswer((_) async => testMember); // when final result = await memberService.addMember(newMemberData); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/add', data: newMemberData, )).called(1); expect(result.id, testMemberId); expect(memberService.members.length, 1); expect(memberService.members[0].id, testMemberId); test('updateMember 메서드가 회원 정보를 업데이트해야 함', () async { // given // API 서비스 모킹 생성 final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: testClubId, }); // 클럽 회원 목록 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 이벤트 발생 시 new_club_id에 대한 모킹 추가 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 클럽 ID 설정 await memberService.setClubId(testClubId); // 먼저 회원 목록 가져오기 await memberService.fetchClubMembers(); // 초기화 후 모든 호출 기록 초기화 clearInteractions(mockApiService); final updates = { 'name': '업데이트된 회원', }; when(mockApiService.post( '${ApiConfig.clubs}/members/update', data: {'clubId': testClubId, 'memberId': testMemberId, ...updates}, )).thenAnswer((_) async => { 'member': { ...testMember, 'name': '업데이트된 회원', } }); // when final result = await memberService.updateMember(testMemberId, updates); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/update', data: {'clubId': testClubId, 'memberId': testMemberId, ...updates}, )).called(1); expect(result.name, '업데이트된 회원'); expect(memberService.members[0].name, '업데이트된 회원'); }); test('updateMember 메서드가 직접 회원 객체가 반환되는 경우도 처리해야 함', () async { // given // API 서비스 모킹 생성 final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: testClubId, }); // 클럽 회원 목록 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 이벤트 발생 시 new_club_id에 대한 모킹 추가 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 클럽 ID 설정 await memberService.setClubId(testClubId); // 먼저 회원 목록 가져오기 await memberService.fetchClubMembers(); final updates = {'name': '업데이트된 회원'}; final updatedMember = Map.from(testMember); updatedMember['name'] = '업데이트된 회원'; // 직접 회원 객체 반환 시ミ레이션 when(mockApiService.post( '${ApiConfig.clubs}/members/update', data: {'clubId': testClubId, 'memberId': testMemberId, ...updates}, )).thenAnswer((_) async => updatedMember); // when final result = await memberService.updateMember(testMemberId, updates); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/update', data: {'clubId': testClubId, 'memberId': testMemberId, ...updates}, )).called(1); expect(result.name, '업데이트된 회원'); expect(memberService.members[0].name, '업데이트된 회원'); }); test('deleteMember 메서드가 회원을 삭제해야 함', () async { // given // API 서비스 모킹 생성 final mockApiService = MockApiService(); when(mockApiService.setToken(testToken)).thenReturn(null); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.tokenKey: testToken, ApiConfig.clubIdKey: testClubId, }); // 클럽 회원 목록 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': testClubId}, )).thenAnswer((_) async => [testMember]); // 이벤트 발생 시 new_club_id에 대한 모킹 추가 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': 'new_club_id'}, )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 클럽 ID 설정 await memberService.setClubId(testClubId); // 먼저 회원 목록 가져오기 await memberService.fetchClubMembers(); when(mockApiService.post( '${ApiConfig.clubs}/members/delete', data: {'memberId': testMemberId, 'clubId': testClubId}, )).thenAnswer((_) async => {'success': true}); // when final result = await memberService.deleteMember(testMemberId); // then verify(mockApiService.post( '${ApiConfig.clubs}/members/delete', data: {'memberId': testMemberId, 'clubId': testClubId}, )).called(1); expect(result, true); expect(memberService.members.length, 0); }); test('ClubChangedEvent가 발생하면 setClubId가 호출되어야 함', () async { // given final initialClubId = 'initial_club_id'; final newClubId = 'new_club_id'; // API 서비스 모킹 생성 final mockApiService = MockApiService(); // SharedPreferences 모킹 설정 SharedPreferences.setMockInitialValues({ ApiConfig.clubIdKey: initialClubId, ApiConfig.tokenKey: testToken, }); // 토큰 설정 when(mockApiService.setToken(testToken)).thenReturn(null); // 초기 클럽 ID에 대한 호출 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': initialClubId}, )).thenAnswer((_) async => [testMember]); // 새 클럽 ID에 대한 호출 모킹 when(mockApiService.post( '${ApiConfig.clubs}/members', data: {'clubId': newClubId}, )).thenAnswer((_) async => [testMember]); // 어떤 인자로든 post 호출에 대한 기본 응답 설정 - 명명된 매개변수에 대한 올바른 mockito 문법 사용 when(mockApiService.post( any, data: anyNamed('data'), )).thenAnswer((_) async => [testMember]); // MemberService 생성 및 초기화 final memberService = MemberService.forTest(mockApiService); memberService.initialize(testToken); // 초기화 후 지연 추가 await Future.delayed(Duration(milliseconds: 500)); // 초기 클럽 ID로 회원 목록 가져오기 await memberService.fetchClubMembers(); // 초기화 후 모든 호출 확인 초기화 clearInteractions(mockApiService); // when - setClubId 호출 await memberService.setClubId(newClubId); // 비동기 처리를 위한 지연 추가 await Future.delayed(Duration(milliseconds: 500)); // then - 실제 MemberService에서 API 호출 확인 // setClubId 호출 후 fetchClubMembers가 호출되어야 함 verify(mockApiService.post( any, data: anyNamed('data'), )).called(greaterThanOrEqualTo(1)); }); }); }