import 'package:flutter_test/flutter_test.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:timezone/data/latest.dart' as tz_data; import 'package:integration_test/integration_test.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:lanebow/models/event_model.dart'; import 'package:lanebow/services/notification_service.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'notification_integration_test.mocks.dart'; @GenerateMocks([ FlutterLocalNotificationsPlugin, AndroidFlutterLocalNotificationsPlugin, IOSFlutterLocalNotificationsPlugin, ]) void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); // timezone 초기화 tz_data.initializeTimeZones(); late MockFlutterLocalNotificationsPlugin mockNotificationsPlugin; late MockAndroidFlutterLocalNotificationsPlugin mockAndroidPlugin; late MockIOSFlutterLocalNotificationsPlugin mockIOSPlugin; late NotificationService originalNotificationService; setUp(() { // 목 플러그인 생성 mockNotificationsPlugin = MockFlutterLocalNotificationsPlugin(); mockAndroidPlugin = MockAndroidFlutterLocalNotificationsPlugin(); mockIOSPlugin = MockIOSFlutterLocalNotificationsPlugin(); // 원래 인스턴스 저장 originalNotificationService = NotificationService(); // 목 플러그인 설정 when(mockNotificationsPlugin.resolvePlatformSpecificImplementation()) .thenReturn(mockAndroidPlugin); when(mockNotificationsPlugin.resolvePlatformSpecificImplementation()) .thenReturn(mockIOSPlugin); // 권한 요청 목업 when(mockIOSPlugin.requestPermissions( alert: true, badge: true, sound: true, critical: false, provisional: false, )).thenAnswer((_) async => true); // 알림 예약 목업 - 모든 matcher 제거하고 실제 값만 사용 when(mockNotificationsPlugin.zonedSchedule( 0, // 알림 ID '알림 제목', '알림 내용', tz.TZDateTime.now(tz.local).add(const Duration(hours: 1)), const NotificationDetails( android: AndroidNotificationDetails( 'event_reminder_channel', '이벤트 리마인더', channelDescription: '예정된 이벤트에 대한 알림을 표시합니다', importance: Importance.high, priority: Priority.high, ), iOS: DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, ), ), androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, matchDateTimeComponents: DateTimeComponents.time, payload: 'test-payload', )).thenAnswer((_) async {}); // 테스트용 NotificationService 생성 및 설정 // 새로 추가한 createTestInstance 메서드를 사용하여 mock 플러그인을 주입 final testNotificationService = NotificationService.createTestInstance(mockNotificationsPlugin); NotificationService.setTestInstance(testNotificationService); }); tearDown(() { // 테스트 후 원래 NotificationService 복원 NotificationService.setTestInstance(originalNotificationService); }); group('알림 서비스 통합 테스트', () { test('알림 권한 요청 기능이 작동해야 함', () async { // NotificationService 테스트 인스턴스 생성 및 설정 final notificationService = NotificationService(); NotificationService.setTestInstance(notificationService); // given when(mockNotificationsPlugin.initialize(any, onDidReceiveNotificationResponse: anyNamed('onDidReceiveNotificationResponse'))) .thenAnswer((_) async => true); when(mockIOSPlugin.requestPermissions( alert: true, badge: true, sound: true, critical: false, provisional: false, )).thenAnswer((_) async => true); // when - 위젯 테스트 대신 직접 메서드 호출 await notificationService.requestPermission(); // then - 권한 요청 메서드가 호출되었는지 확인 verify(mockIOSPlugin.requestPermissions( alert: true, badge: true, sound: true, critical: false, provisional: false, )).called(1); }); test('이벤트 알림 설정 기능이 작동해야 함', () async { // given when(mockNotificationsPlugin.initialize(any, onDidReceiveNotificationResponse: anyNamed('onDidReceiveNotificationResponse'))) .thenAnswer((_) async => true); // 테스트용 이벤트 생성 final testEvent = Event( id: 'test_event_id', clubId: 'test_club_id', title: '테스트 이벤트', description: '테스트 이벤트 설명', startDate: DateTime.now().add(const Duration(hours: 2)), endDate: DateTime.now().add(const Duration(hours: 4)), location: '테스트 장소', maxParticipants: 10, gameCount: 3, participantFee: 10000, registrationDeadline: DateTime.now().add(const Duration(days: 2)), isActive: true, createdBy: 'test_user_id', type: '팀전', ); // NotificationService 테스트 인스턴스 생성 및 설정 final notificationService = NotificationService(); NotificationService.setTestInstance(notificationService); // zonedSchedule 메서드 stub 설정 - any matcher 사용 when(mockNotificationsPlugin.zonedSchedule( any, any, any, any, any, androidScheduleMode: anyNamed('androidScheduleMode'), matchDateTimeComponents: anyNamed('matchDateTimeComponents'), payload: anyNamed('payload'), )).thenAnswer((_) async {}); // when - 알림 설정 메서드 직접 호출 await notificationService.scheduleEventStartReminder(testEvent); // then - zonedSchedule이 호출되었는지 확인 verify(mockNotificationsPlugin.zonedSchedule( any, '이벤트 시작 알림', '${testEvent.title} 이벤트가 1시간 후에 시작됩니다.', any, any, androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, matchDateTimeComponents: DateTimeComponents.time, payload: testEvent.id, )).called(1); }); }); } // 테스트를 위한 NotificationService 확장 클래스 class TestNotificationService implements NotificationService { final FlutterLocalNotificationsPlugin mockNotificationsPlugin; bool _isInitialized = false; TestNotificationService(this.mockNotificationsPlugin); FlutterLocalNotificationsPlugin get _notificationsPlugin => mockNotificationsPlugin; @override bool get isInitialized => _isInitialized; @override set isInitialized(bool value) { _isInitialized = value; } @override Future initialize() async { _isInitialized = true; return Future.value(); } @override Future requestPermission() async { return Future.value(true); } @override Future showNotification({required int id, required String title, required String body, String? payload}) { return Future.value(); } @override Future scheduleNotification({required int id, required String title, required String body, required DateTime scheduledDate, String? payload}) { return Future.value(); } @override Future scheduleEventStartReminder(Event event) { return Future.value(); } @override Future scheduleRegistrationDeadlineReminder(Event event) { return Future.value(); } @override Future cancelEventNotifications(String eventId) { return Future.value(); } @override Future cancelAllNotifications() { return Future.value(); } } // NotificationService에 테스트 인스턴스를 설정하기 위한 확장 extension NotificationServiceTestExtension on NotificationService { static void setTestInstance(NotificationService instance) { // 이 메서드는 실제 구현에서는 작동하지 않지만, 테스트 목적으로 추가 // 실제로는 NotificationService 클래스에 이 기능을 추가해야 함 // 테스트에서는 이 메서드가 호출될 때 NotificationService._instance를 교체하는 방식으로 동작해야 함 } } // MockIOSFlutterLocalNotificationsPlugin은 이제 @GenerateMocks에 의해 자동 생성됩니다.