이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입
This commit is contained in:
@@ -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']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user