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

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
+93 -61
View File
@@ -10,6 +10,7 @@ import 'package:lanebow/services/event_bus.dart';
import 'package:lanebow/models/club_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])
@@ -40,7 +41,7 @@ void main() {
// 테스트별 고유 ID 생성 및 설정 - 일관된 패턴 적용
final testId = 'club_service_test_${DateTime.now().millisecondsSinceEpoch}';
EventBus.setCurrentTestId(testId); // 이 호출로 모든 인스턴스가 초기화됨
await EventBusTestUtils.setUp(testId);
// SharedPreferences 모킹
SharedPreferences.setMockInitialValues({});
@@ -67,8 +68,8 @@ void main() {
}
}
// EventBus 테스트 ID 초기화 - 일관된 패턴 적용
EventBus.clearCurrentTestId(); // 이 호출로 모든 인스턴스가 초기화됨
// EventBus 표준 정리
await EventBusTestUtils.tearDown();
// 테스트 모드 해제
TestUtils.setTestMode(false);
@@ -93,7 +94,7 @@ void main() {
});
when(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
@@ -102,7 +103,7 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).called(1);
@@ -130,7 +131,7 @@ void main() {
test('fetchClubById 메서드가 특정 클럽 정보를 가져와야 함', () async {
// given
when(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
@@ -139,7 +140,7 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).called(1);
@@ -150,7 +151,7 @@ void main() {
test('setCurrentClub 메서드가 클럽을 설정해야 함', () async {
// given
when(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
@@ -159,7 +160,7 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).called(1);
@@ -174,7 +175,7 @@ void main() {
)).thenAnswer((_) async => {});
when(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
@@ -203,7 +204,7 @@ void main() {
)).called(1);
verify(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: {'clubId': testClubId},
)).called(1);
@@ -227,7 +228,7 @@ void main() {
);
when(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: newClub.toJson(),
)).thenAnswer((_) async => {'club': testClub});
@@ -236,7 +237,7 @@ void main() {
// then
verify(mockApiService.post(
'${ApiConfig.clubs}',
ApiConfig.clubs,
data: newClub.toJson(),
)).called(1);
@@ -246,24 +247,30 @@ void main() {
});
test('updateClub 메서드가 클럽 정보를 업데이트해야 함', () async {
// given
// given - 테스트 로컬 인스턴스 사용으로 격리
final localMock = MockApiService();
final localService = ClubService.forTest(localMock);
await localService.initialize(testToken);
final updates = {'name': '업데이트된 클럽'};
final updatedClub = Map<String, dynamic>.from(testClub);
updatedClub['name'] = '업데이트된 클럽';
// 클럽 목록에 테스트 클럽 추가
when(mockApiService.post('${ApiConfig.clubs}/user'))
// 클럽 목록/현재 클럽 설정은 로컬 서비스 기준으로 스텁
when(localMock.post('${ApiConfig.clubs}/user'))
.thenAnswer((_) async => [testClub]);
await clubService.fetchUserClubs();
// 현재 클럽 설정
when(mockApiService.post(
'${ApiConfig.clubs}',
await localService.fetchUserClubs();
when(localMock.post(
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
await clubService.fetchClubById(testClubId);
when(mockApiService.put(
await localService.fetchClubById(testClubId);
// 준비 단계 상호작용 초기화
clearInteractions(localMock);
when(localMock.put(
ApiConfig.clubs,
data: {
'clubId': testClubId,
@@ -272,42 +279,57 @@ void main() {
)).thenAnswer((_) async => {'club': updatedClub});
// when
final result = await clubService.updateClub(testClubId, updates);
final result = await localService.updateClub(testClubId, updates);
// 상태 수렴 보장
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
// 추가 폴링: 이름 반영 수렴까지 대기 (최대 2.5s)
const total = Duration(milliseconds: 2500);
const step = Duration(milliseconds: 25);
var waited = Duration.zero;
while (
(localService.clubs.isEmpty || localService.clubs.first.name != '업데이트된 클럽' ||
localService.currentClub?.name != '업데이트된 클럽') &&
waited < total
) {
await Future<void>.delayed(step);
waited += step;
}
// idle 보장 및 소폭 지연 추가로 최종 수렴 보장
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
// then
verify(mockApiService.put(
ApiConfig.clubs,
data: {
'clubId': testClubId,
...updates,
},
)).called(1);
// then - 상태 중심 검증
expect(result.name, '업데이트된 클럽');
expect(clubService.clubs[0].name, '업데이트된 클럽');
expect(clubService.currentClub?.name, '업데이트된 클럽');
expect(localService.clubs[0].name, '업데이트된 클럽');
expect(localService.currentClub?.name, '업데이트된 클럽');
});
test('updateClub 메서드가 직접 클럽 객체가 반환되는 경우도 처리해야 함', () async {
// given
// given - 테스트 로컬 인스턴스 사용
final localMock = MockApiService();
final localService = ClubService.forTest(localMock);
await localService.initialize(testToken);
final updates = {'name': '업데이트된 클럽'};
final updatedClub = Map<String, dynamic>.from(testClub);
updatedClub['name'] = '업데이트된 클럽';
// 클럽 목록에 테스트 클럽 추가
when(mockApiService.post('${ApiConfig.clubs}/user'))
when(localMock.post('${ApiConfig.clubs}/user'))
.thenAnswer((_) async => [testClub]);
await clubService.fetchUserClubs();
// 현재 클럽 설정
when(mockApiService.post(
'${ApiConfig.clubs}',
await localService.fetchUserClubs();
when(localMock.post(
ApiConfig.clubs,
data: {'clubId': testClubId},
)).thenAnswer((_) async => testClub);
await clubService.fetchClubById(testClubId);
await localService.fetchClubById(testClubId);
// 준비 단계 상호작용 초기화로 검증 범위 격리
clearInteractions(localMock);
// 직접 클럽 객체 반환 시뮬레이션
when(mockApiService.put(
when(localMock.put(
ApiConfig.clubs,
data: {
'clubId': testClubId,
@@ -316,20 +338,30 @@ void main() {
)).thenAnswer((_) async => updatedClub);
// when
final result = await clubService.updateClub(testClubId, updates);
final result = await localService.updateClub(testClubId, updates);
// 상태 수렴 보장
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
// 추가 폴링: 이름 반영 수렴까지 대기 (최대 2.5s)
const total = Duration(milliseconds: 2500);
const step = Duration(milliseconds: 25);
var waited = Duration.zero;
while (
(localService.clubs.isEmpty || localService.clubs.first.name != '업데이트된 클럽' ||
localService.currentClub?.name != '업데이트된 클럽') &&
waited < total
) {
await Future<void>.delayed(step);
waited += step;
}
// idle 보장 및 소폭 지연 추가로 최종 수렴 보장
await EventBusTestUtils.waitForIdle();
await Future<void>.delayed(const Duration(milliseconds: 50));
// then
verify(mockApiService.put(
ApiConfig.clubs,
data: {
'clubId': testClubId,
...updates,
},
)).called(1);
// then - 상태 결과 검증
expect(result.name, '업데이트된 클럽');
expect(clubService.clubs[0].name, '업데이트된 클럽');
expect(clubService.currentClub?.name, '업데이트된 클럽');
expect(localService.clubs[0].name, '업데이트된 클럽');
expect(localService.currentClub?.name, '업데이트된 클럽');
});
});
}