이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입

This commit is contained in:
2025-09-12 06:33:44 +09:00
parent 107abc963f
commit a5f2544d11
121 changed files with 39277 additions and 3911 deletions
+221 -31
View File
@@ -4,12 +4,9 @@ import 'package:mockito/annotations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:lanebow/services/event_service.dart';
import 'package:lanebow/services/api_service.dart';
import 'package:lanebow/services/event_bus.dart';
import 'package:lanebow/models/event_model.dart';
import 'package:lanebow/models/participant_model.dart';
import 'package:lanebow/models/score_model.dart';
import 'package:lanebow/config/api_config.dart';
import 'package:lanebow/utils/test_utils.dart';
import '../utils/event_bus_test_utils.dart';
// 모킹 클래스 생성
@GenerateMocks([ApiService])
@@ -68,10 +65,7 @@ void main() {
// 테스트별 고유 ID 생성 및 설정
final testId = 'event_service_test_${DateTime.now().millisecondsSinceEpoch}';
EventBus.setCurrentTestId(testId);
// EventBus 완전 초기화
EventBus().reset();
await EventBusTestUtils.setUp(testId);
// SharedPreferences 모킹
SharedPreferences.setMockInitialValues({
@@ -91,15 +85,12 @@ void main() {
eventService.setClubId(testClubId);
});
tearDown(() {
// 테스트 종료 후 EventBus 초기화
EventBus().reset();
tearDown(() async {
// 테스트에서 생성된 EventService 정리
eventService.dispose();
try { eventService.dispose(); } catch (_) {}
// 테스트 ID 초기화
EventBus.clearCurrentTestId();
// EventBus 표준 정리
await EventBusTestUtils.tearDown();
// 테스트 모드 해제
TestUtils.setTestMode(false);
@@ -248,8 +239,8 @@ void main() {
final testParticipants = [testParticipant];
when(mockApiService.post(
'${ApiConfig.clubs}/events/participants',
data: {'eventId': eventId},
'${ApiConfig.clubs}/events/$eventId/participants/list',
data: anyNamed('data'),
)).thenAnswer((_) async => {'participants': testParticipants});
// when
@@ -257,8 +248,8 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}/events/participants',
data: {'eventId': eventId},
'${ApiConfig.clubs}/events/$eventId/participants/list',
data: anyNamed('data'),
)).called(1);
expect(result.length, 1);
@@ -274,9 +265,10 @@ void main() {
'status': 'confirmed',
};
// 서비스는 clubId를 payload에 병합하므로 포함하여 검증
when(mockApiService.post(
'${ApiConfig.clubs}/events/$eventId/participants',
data: participantData,
data: anyNamed('data'),
)).thenAnswer((_) async => {'participant': testParticipant});
// when
@@ -285,7 +277,7 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}/events/$eventId/participants',
data: participantData,
data: anyNamed('data'),
)).called(1);
expect(result.id, 'test_participant_id');
@@ -296,20 +288,20 @@ void main() {
// given
final eventId = 'test_event_id';
final participantId = 'test_participant_id';
final updates = {'status': 'cancelled'};
final updates = {'status': 'canceled'};
final updatedParticipant = Map<String, dynamic>.from(testParticipant);
updatedParticipant['status'] = 'cancelled';
updatedParticipant['status'] = 'canceled';
// 참가자 목록에 테스트 참가자 추가
when(mockApiService.post(
'${ApiConfig.clubs}/events/participants',
data: {'eventId': eventId},
'${ApiConfig.clubs}/events/$eventId/participants/list',
data: anyNamed('data'),
)).thenAnswer((_) async => {'participants': [testParticipant]});
await eventService.fetchEventParticipants(eventId);
when(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/participants/$participantId',
data: updates,
data: anyNamed('data'),
)).thenAnswer((_) async => {'participant': updatedParticipant});
// when
@@ -318,11 +310,11 @@ void main() {
// then
verify(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/participants/$participantId',
data: updates,
data: anyNamed('data'),
)).called(1);
expect(result.status, 'cancelled');
expect(eventService.participants[0].status, 'cancelled');
expect(result.status, 'canceled');
expect(eventService.participants[0].status, 'canceled');
});
test('removeParticipant 메서드가 참가자를 삭제해야 함', () async {
@@ -332,8 +324,8 @@ void main() {
// 참가자 목록에 테스트 참가자 추가
when(mockApiService.post(
'${ApiConfig.clubs}/events/participants',
data: {'eventId': eventId},
'${ApiConfig.clubs}/events/$eventId/participants/list',
data: anyNamed('data'),
)).thenAnswer((_) async => {'participants': [testParticipant]});
await eventService.fetchEventParticipants(eventId);
@@ -403,5 +395,203 @@ void main() {
expect(result.id, 'test_score_id');
expect(eventService.scores.length, 1);
});
test('updateScore 메서드가 점수를 업데이트해야 함', () async {
// given
final eventId = 'test_event_id';
final scoreId = 'test_score_id';
final updates = {
'games': [190, 210, 230],
'handicap': 5,
};
// 초기 점수 목록 적재
when(mockApiService.post(
'${ApiConfig.clubs}/events/scores',
data: {'eventId': eventId},
)).thenAnswer((_) async => {'scores': [testScore]});
await eventService.fetchEventScores(eventId);
final updated = Map<String, dynamic>.from(testScore);
updated['games'] = [190, 210, 230];
updated['handicap'] = 5;
updated['total'] = 635; // 예시 합계
updated['average'] = 211.67;
when(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/scores/$scoreId',
data: updates,
)).thenAnswer((_) async => {'score': updated});
// when
final result = await eventService.updateScore(eventId, scoreId, updates);
// then
verify(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/scores/$scoreId',
data: updates,
)).called(1);
expect(result.handicap, 5);
expect(eventService.scores.first.handicap, 5);
});
test('deleteScore 메서드가 점수를 삭제해야 함', () async {
// given
final eventId = 'test_event_id';
final scoreId = 'test_score_id';
// 초기 점수 목록 적재
when(mockApiService.post(
'${ApiConfig.clubs}/events/scores',
data: {'eventId': eventId},
)).thenAnswer((_) async => {'scores': [testScore]});
await eventService.fetchEventScores(eventId);
when(mockApiService.delete(
'${ApiConfig.clubs}/events/$eventId/scores/$scoreId',
)).thenAnswer((_) async => {});
// when
final result = await eventService.deleteScore(eventId, scoreId);
// then
verify(mockApiService.delete(
'${ApiConfig.clubs}/events/$eventId/scores/$scoreId',
)).called(1);
expect(result, true);
expect(eventService.scores.length, 0);
});
// 실패 케이스: 참가자 추가
test('addParticipant 실패 시 예외 메시지와 로딩 상태 해제 및 목록 불변', () async {
// given
final eventId = 'test_event_id';
final participantData = {
'memberId': 'test_member_id',
'status': 'confirmed',
};
when(mockApiService.post(
'${ApiConfig.clubs}/events/$eventId/participants',
data: anyNamed('data'),
)).thenThrow(Exception('API 오류'));
// when
expect(
() => eventService.addParticipant(eventId, participantData),
throwsA(isA<Exception>().having((e) => e.toString(), 'message', contains('참가자 추가에 실패했습니다'))),
);
// then
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(eventService.isLoading, false);
expect(eventService.participants.length, 0);
});
// 실패 케이스: 참가자 업데이트
test('updateParticipant 실패 시 예외 메시지와 로딩 상태 해제 및 목록 불변', () async {
// given
final eventId = 'test_event_id';
final participantId = 'test_participant_id';
final updates = {'status': 'canceled'};
// 기존 목록 적재
when(mockApiService.post(
'${ApiConfig.clubs}/events/$eventId/participants/list',
data: {'clubId': testClubId, 'eventId': eventId},
)).thenAnswer((_) async => {'participants': [testParticipant]});
await eventService.fetchEventParticipants(eventId);
when(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/participants/$participantId',
data: anyNamed('data'),
)).thenThrow(Exception('API 오류'));
// when
expect(
() => eventService.updateParticipant(eventId, participantId, updates),
throwsA(isA<Exception>().having((e) => e.toString(), 'message', contains('참가자 정보 업데이트에 실패했습니다'))),
);
// then
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(eventService.isLoading, false);
// 실패 시 기존 상태 유지
expect(eventService.participants.first.status, testParticipant['status']);
});
// 실패 케이스: 점수 추가
test('addScore 실패 시 예외 메시지와 로딩 상태 해제 및 목록 불변', () async {
// given
final eventId = 'test_event_id';
final scoreData = {
'participantId': 'test_participant_id',
'games': [180, 200, 220],
'handicap': 10,
};
when(mockApiService.post(
'${ApiConfig.clubs}/events/$eventId/scores',
data: scoreData,
)).thenThrow(Exception('API 오류'));
// when
expect(
() => eventService.addScore(eventId, scoreData),
throwsA(isA<Exception>().having((e) => e.toString(), 'message', contains('점수 추가에 실패했습니다'))),
);
// then
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
expect(eventService.isLoading, false);
expect(eventService.scores.length, 0);
});
// 실패 케이스: 점수 업데이트
test('updateScore 실패 시 예외 메시지와 로딩 상태 해제 및 목록 불변', () async {
// given
final eventId = 'test_event_id';
final scoreId = 'test_score_id';
final updates = {
'games': [190, 210, 230],
'handicap': 5,
};
// 기존 목록 적재
when(mockApiService.post(
'${ApiConfig.clubs}/events/scores',
data: {'eventId': eventId},
)).thenAnswer((_) async => {'scores': [testScore]});
await eventService.fetchEventScores(eventId);
when(mockApiService.put(
'${ApiConfig.clubs}/events/$eventId/scores/$scoreId',
data: updates,
)).thenThrow(Exception('API 오류'));
// when
expect(
() => eventService.updateScore(eventId, scoreId, updates),
throwsA(isA<Exception>().having((e) => e.toString(), 'message', contains('점수 업데이트에 실패했습니다'))),
);
// then
await EventBusTestUtils.waitForIdle();
// isLoading=false 수렴까지 폴링 대기 (최대 1.5s)
const total = Duration(milliseconds: 1500);
const step = Duration(milliseconds: 25);
var waited = Duration.zero;
while (eventService.isLoading && waited < total) {
await Future<void>.delayed(step);
waited += step;
}
expect(eventService.isLoading, false);
// 실패 시 기존 상태 유지
expect(eventService.scores.first.id, testScore['id']);
});
});
}