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

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
@@ -0,0 +1,209 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:provider/provider.dart';
import 'package:lanebow/screens/club/event_details_screen.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/models/member_model.dart';
import 'package:lanebow/models/club_model.dart';
import 'package:lanebow/models/team_model.dart';
import 'package:lanebow/services/event_service.dart';
import 'package:lanebow/services/club_service.dart';
import 'package:lanebow/services/member_service.dart';
import 'package:lanebow/widgets/loading_indicator.dart';
// 간단한 테스트 더블 구현 (ChangeNotifier 유지)
class FakeEventService extends EventService {
@override
List<Participant> participants = [];
@override
List<Score> scores = [];
@override
Future<List<Participant>> fetchEventParticipants(String eventId) async {
return participants;
}
@override
Future<List<Score>> fetchEventScores(String eventId) async {
return scores;
}
@override
Future<List<Team>> fetchEventTeams(String eventId) async {
// 팀 기능은 테스트에서 필수가 아니므로 빈 리스트를 즉시 반환하여 네트워크 의존성을 제거
return [];
}
}
class FakeClubService extends ClubService {
Club? club;
@override
Future<Club> fetchClub(String clubId) async {
return club ?? Club(name: '클럽', id: clubId);
}
}
class FakeMemberService extends MemberService {
List<Member> membersByIds = [];
@override
Future<List<Member>> fetchMembersByIds(String clubId, List<String> ids) async {
return membersByIds;
}
@override
Future<void> setClubId(String clubId) async {
// no-op: 테스트에서는 내부 상태 주입만 필요하며 네트워크 호출 불필요
return;
}
@override
Future<void> fetchClubMembers() async {
// no-op: 테스트에서는 네트워크 호출 없이 즉시 완료
return;
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
// ChangeNotifier 서브타입에 Provider.value 사용 허용(자동 업데이트 불필요한 테스트 시나리오)
Provider.debugCheckInvalidValueType = null;
group('EventDetailsScreen', () {
late FakeEventService eventService;
late FakeClubService clubService;
late FakeMemberService memberService;
final event = Event(
id: 'e1',
clubId: 'c1',
title: '테스트 이벤트',
description: '설명입니다',
startDate: DateTime(2025, 1, 2, 19, 30),
endDate: DateTime(2025, 1, 2, 22, 00),
location: '강남볼링장',
type: '정기전',
status: '진행중',
maxParticipants: 24,
currentParticipants: 2,
gameCount: 3,
participantFee: 10000,
registrationDeadline: DateTime(2024, 12, 31, 23, 59),
publicHash: 'hash123',
accessPassword: '1234',
isActive: true,
);
setUp(() {
eventService = FakeEventService();
clubService = FakeClubService();
memberService = FakeMemberService();
// Participants
eventService.participants = [
Participant(
id: 'p1',
eventId: 'e1',
memberId: 'm1',
registeredAt: DateTime(2025, 1, 1),
),
Participant(
id: 'p2',
eventId: 'e1',
memberId: 'm2',
registeredAt: DateTime(2025, 1, 1),
),
];
// Scores
eventService.scores = [
Score(
id: 's1',
eventId: 'e1',
memberId: 'm1',
clubId: 'c1',
frames: const [100, 100, 100, 100, 100],
totalScore: 500,
handicap: 0,
date: DateTime(2025, 1, 2),
),
];
// Club
clubService.club = Club(id: 'c1', name: '볼링클럽', ownerId: 'u1');
// Members
memberService.membersByIds = [
Member(
id: 'm1',
userId: 'u1',
clubId: 'c1',
name: '철수',
email: 'a@b.com',
isActive: true,
gender: 'M',
birthDate: DateTime(1990, 1, 1),
),
Member(
id: 'm2',
userId: 'u2',
clubId: 'c1',
name: '영희',
email: 'c@d.com',
isActive: true,
gender: 'F',
birthDate: DateTime(1992, 1, 1),
),
];
});
Widget wrapWithProviders(Widget child) {
return MultiProvider(
providers: [
Provider<EventService>.value(value: eventService),
Provider<ClubService>.value(value: clubService),
Provider<MemberService>.value(value: memberService),
],
child: MaterialApp(home: child),
);
}
testWidgets('로딩 후 기본 정보 탭 렌더링 및 핵심 정보 표시', (tester) async {
await tester.pumpWidget(wrapWithProviders(EventDetailsScreen(event: event)));
// 초기 로딩 표시
expect(find.byType(LoadingIndicator), findsOneWidget);
// async 로딩/초기 빌드가 끝나도록 약간 더 대기 (무한 대기 방지)
await tester.pump(const Duration(milliseconds: 200));
await tester.pump(const Duration(milliseconds: 200));
// AppBar 제목
expect(find.text('테스트 이벤트'), findsOneWidget);
// 탭 존재 확인
expect(find.text('기본 정보'), findsOneWidget);
expect(find.text('참가자'), findsOneWidget);
expect(find.text('점수'), findsOneWidget);
expect(find.text(''), findsOneWidget);
// 기본 정보 섹션이 렌더링되고 공유 버튼이 존재
expect(find.byIcon(Icons.share), findsOneWidget);
// 장소/날짜와 같은 텍스트 일부가 표시되는지(정확한 포맷 대신 포함 여부만 확인)
expect(find.textContaining('강남'), findsWidgets);
});
testWidgets('참가자 탭: 참가자 수와 리스트 표시', (tester) async {
await tester.pumpWidget(wrapWithProviders(EventDetailsScreen(event: event)));
await tester.pump(const Duration(milliseconds: 50));
await tester.pump(const Duration(milliseconds: 50));
// 참가자 탭으로 이동: 탭 터치 대신 TabBarView를 드래그로 넘김(제스처 경로 단순화)
await tester.drag(find.byType(TabBarView), const Offset(-400, 0));
await tester.pump(const Duration(milliseconds: 250));
await tester.pump(const Duration(milliseconds: 250));
// 리스트가 렌더링되는지(정확한 항목 텍스트 대신 리스트 존재 확인)
expect(find.byType(ListView), findsWidgets);
});
});
}