이벤트 가져오기 위젯 테스트 추가 및 테스트 훅 도입
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# Test Utilities Guide
|
||||
|
||||
본 문서는 Flutter 위젯/서비스 테스트를 안정적으로 작성하기 위한 공용 유틸 사용 가이드를 제공합니다. 실제 예시는 프로젝트 내 테스트 코드에서 그대로 복사해 사용할 수 있습니다.
|
||||
|
||||
## 구성 요소
|
||||
|
||||
- test/utils/mock_services.dart
|
||||
- test/utils/stub_event_service.dart
|
||||
- test/utils/provider_presets.dart
|
||||
- test/utils/widget_test_utils.dart (향후 확장 시)
|
||||
- test/utils/event_bus_test_utils.dart
|
||||
|
||||
## 공통 패턴
|
||||
|
||||
- __오버레이/네비 의존 제거__: 팝업/스낵바/네비게이션은 테스트에서 `pumpAndSettle`를 가로막는 대표 원인입니다. 가능하면 화면에 `@visibleForTesting` 훅(예: `invokeSaveForTest`, `invokeDuplicateForTest`)을 제공하여 내부 로직을 직접 호출하세요.
|
||||
- __pumpAndSettle 지양__: 무한 대기 가능성이 있으므로, `await tester.pump()` + `await tester.pump(const Duration(milliseconds: 50))` 조합을 우선적으로 사용합니다.
|
||||
- __ensureVisible__: 탭/토글/입력 전에는 `await tester.ensureVisible(finder)`로 화면 내 가시성을 보장합니다.
|
||||
- __Key 부여__: 테스트가 찾는 주요 위젯에는 `ValueKey`를 부여합니다. 예) `ValueKey('access_password_field')`, `ValueKey('event_menu_<id>')`.
|
||||
- __SharedPreferences 모킹__: 영속 값(검색어 등)을 사용 시 `SharedPreferences.setMockInitialValues({...})`를 통해 초기 상태를 정의합니다.
|
||||
|
||||
## Mock/Stub 소개
|
||||
|
||||
### MockAuthService / MockClubService (`test/utils/mock_services.dart`)
|
||||
- __MockAuthService__
|
||||
- 고정 토큰과 사용자 정보(`User`)를 제공합니다.
|
||||
- `isAuthenticated`, `currentUser`, `token` 등을 간단히 반환합니다.
|
||||
- __MockClubService__
|
||||
- 단일 `Club`을 보유하고 `currentClub`/`clubs`를 제공합니다.
|
||||
- `updateClub()`는 `ownerId` 반영 정도만 최소 구현했습니다.
|
||||
|
||||
사용 예시:
|
||||
```dart
|
||||
final auth = MockAuthService(
|
||||
user: User(id: 'u1', email: 'u@ex.com', name: 'Tester', role: 'owner'),
|
||||
tokenValue: 'token',
|
||||
);
|
||||
final club = MockClubService(
|
||||
club: Club(id: 'club1', name: '클럽', averageCalculationPeriod: '3'),
|
||||
);
|
||||
```
|
||||
|
||||
### StubEventService (`test/utils/stub_event_service.dart`)
|
||||
- 네트워크 호출을 모두 차단하고 즉시 완료되는 `EventService` 대체 구현입니다.
|
||||
- __관찰 필드__
|
||||
- `lastCloneCalled`, `lastClonedEventId`, `lastCreatedEventData`
|
||||
- __시드 이벤트__
|
||||
- `seededEvents`에 `Event` 리스트를 주입해 목록을 렌더링합니다.
|
||||
- __안정화 포인트__
|
||||
- `createEvent/cloneEvent`는 notifyListeners 후 바로 반환합니다.
|
||||
|
||||
사용 예시:
|
||||
```dart
|
||||
final stub = StubEventService();
|
||||
await stub.initialize('token');
|
||||
stub.setClubId('club1');
|
||||
stub.seededEvents = [Event(id: 'e1', clubId: 'club1', title: '타이틀', startDate: DateTime.now(), status: '활성', isActive: true)];
|
||||
```
|
||||
|
||||
### Provider 프리셋 (`test/utils/provider_presets.dart`)
|
||||
- 특정 화면에 필요한 Provider 집합을 손쉽게 구성합니다.
|
||||
- 현재 예시는 ClubSettings 시나리오용으로 제공되며, 필요 시 확장하세요.
|
||||
|
||||
사용 예시:
|
||||
```dart
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<AuthService>.value(value: auth),
|
||||
ChangeNotifierProvider<ClubService>.value(value: club),
|
||||
ChangeNotifierProvider<EventService>.value(value: stub),
|
||||
],
|
||||
child: const MaterialApp(home: EventsScreen()),
|
||||
);
|
||||
```
|
||||
|
||||
## 화면별 테스트 팁
|
||||
|
||||
### EventsScreen
|
||||
- __복제 메뉴 테스트__: `PopupMenuButton` 오버레이를 열지 말고, 상태 훅 `invokeDuplicateForTest(event)`를 호출하세요.
|
||||
- __ellipsis 회귀 테스트__: `Text` 위젯을 `find.textContaining()`으로 찾고, `Text.maxLines`/`Text.overflow`를 직접 검증하세요.
|
||||
|
||||
### EventFormScreen
|
||||
- __저장 테스트__: 저장 버튼 탭 대신 `invokeSaveForTest()` 호출. 테스트 모드에서는 pop/스낵바를 우회하고 `_isLoading`을 해제하는 로직이 포함되어 있어 hang 방지에 유리합니다.
|
||||
- __비밀번호 필드__: `ValueKey('access_password_field')`로 필드 접근, 토글 아이콘은 `Tooltip('비밀번호 표시')`/`('비밀번호 숨김')`으로 찾습니다.
|
||||
|
||||
## EventBus 테스트 유틸 (`test/utils/event_bus_test_utils.dart`)
|
||||
- 각 테스트에서 고유 테스트 ID를 설정하고, setUp/tearDown에서 `EventBus`를 초기화/해제합니다.
|
||||
- 간헐적 경합을 줄이기 위해 tearDown에서 짧은 대기(`Future.delayed`)를 추가합니다.
|
||||
|
||||
샘플 패턴:
|
||||
```dart
|
||||
setUp(() async {
|
||||
final testId = 'member_service_test_${DateTime.now().millisecondsSinceEpoch}';
|
||||
await EventBusTestUtils.setUp(testId);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await EventBusTestUtils.tearDown();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||
});
|
||||
```
|
||||
|
||||
## 권장 사항 요약
|
||||
|
||||
- __핵심__: 오버레이/네비 의존 제거, 테스트 훅 사용, `pumpAndSettle` 지양.
|
||||
- __상태__: `_isLoading`은 성공/실패 모두 해제되도록 보장.
|
||||
- __가시성__: 액션 전 `ensureVisible` 사용.
|
||||
- __모델/타입__: 실제 모델 시그니처에 맞는 파라미터(문자열/정수, const 사용 여부 등) 확인.
|
||||
Reference in New Issue
Block a user