Files
superport/test/helpers/test_helpers.dart
JiWoong Sul 198aac6525
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled
test: 통합 테스트 오류 및 경고 수정
- 모든 서비스 메서드 시그니처를 실제 구현에 맞게 수정
- TestDataGenerator 제거하고 직접 객체 생성으로 변경
- 모델 필드명 및 타입 불일치 수정
- 불필요한 Either 패턴 사용 제거
- null safety 관련 이슈 해결

수정된 파일:
- test/integration/screens/company_integration_test.dart
- test/integration/screens/equipment_integration_test.dart
- test/integration/screens/user_integration_test.dart
- test/integration/screens/login_integration_test.dart
2025-08-05 20:24:05 +09:00

324 lines
8.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:mocktail/mocktail.dart';
// FlutterSecureStorage Mock 클래스
class MockFlutterSecureStorage extends Mock implements FlutterSecureStorage {}
/// 테스트용 GetIt 인스턴스 초기화
GetIt setupTestGetIt() {
final getIt = GetIt.instance;
// 기존 등록된 서비스들 모두 제거
getIt.reset();
// FlutterSecureStorage mock 등록
final mockSecureStorage = MockFlutterSecureStorage();
when(() => mockSecureStorage.read(key: any(named: 'key')))
.thenAnswer((_) async => null);
when(() => mockSecureStorage.write(key: any(named: 'key'), value: any(named: 'value')))
.thenAnswer((_) async {});
when(() => mockSecureStorage.delete(key: any(named: 'key')))
.thenAnswer((_) async {});
when(() => mockSecureStorage.deleteAll())
.thenAnswer((_) async {});
getIt.registerSingleton<FlutterSecureStorage>(mockSecureStorage);
return getIt;
}
/// 테스트용 위젯 래퍼
/// 모든 위젯 테스트에서 필요한 기본 설정을 제공
class TestWidgetWrapper extends StatelessWidget {
final Widget child;
final List<ChangeNotifierProvider>? providers;
final NavigatorObserver? navigatorObserver;
final Map<String, WidgetBuilder>? routes;
final String? initialRoute;
const TestWidgetWrapper({
super.key,
required this.child,
this.providers,
this.navigatorObserver,
this.routes,
this.initialRoute,
});
@override
Widget build(BuildContext context) {
Widget wrappedChild = MaterialApp(
title: 'Test App',
theme: ThemeData(
primarySwatch: Colors.blue,
useMaterial3: true,
),
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [
Locale('ko', 'KR'),
Locale('en', 'US'),
],
home: Scaffold(body: child),
routes: routes ?? {},
initialRoute: initialRoute,
navigatorObservers: navigatorObserver != null ? [navigatorObserver!] : [],
);
// Provider가 있는 경우 래핑
if (providers != null && providers!.isNotEmpty) {
return MultiProvider(
providers: providers!,
child: wrappedChild,
);
}
return wrappedChild;
}
}
/// 위젯을 테스트 환경에서 펌프하는 헬퍼 함수
Future<void> pumpTestWidget(
WidgetTester tester,
Widget widget, {
List<ChangeNotifierProvider>? providers,
NavigatorObserver? navigatorObserver,
Map<String, WidgetBuilder>? routes,
String? initialRoute,
Size? screenSize,
}) async {
// 화면 크기 설정
if (screenSize != null) {
tester.view.physicalSize = screenSize;
tester.view.devicePixelRatio = 1.0;
} else {
// 기본값: 태블릿 크기 (테이블 UI를 위해 충분한 크기)
tester.view.physicalSize = const Size(1024, 768);
tester.view.devicePixelRatio = 1.0;
}
await tester.pumpWidget(
TestWidgetWrapper(
providers: providers,
navigatorObserver: navigatorObserver,
routes: routes,
initialRoute: initialRoute,
child: widget,
),
);
}
/// 비동기 작업을 기다리고 위젯을 리빌드하는 헬퍼
Future<void> pumpAndSettleWithTimeout(
WidgetTester tester, {
Duration timeout = const Duration(seconds: 10),
}) async {
await tester.pump();
await tester.pumpAndSettle(timeout);
}
/// TextField에 텍스트를 입력하는 헬퍼
Future<void> enterTextByLabel(
WidgetTester tester,
String label,
String text,
) async {
final textFieldFinder = find.ancestor(
of: find.text(label),
matching: find.byType(TextFormField),
);
if (textFieldFinder.evaluate().isEmpty) {
// 라벨로 찾지 못한 경우, 가까운 TextFormField 찾기
final textField = find.byType(TextFormField).first;
await tester.enterText(textField, text);
} else {
await tester.enterText(textFieldFinder, text);
}
}
/// 버튼을 찾고 탭하는 헬퍼
Future<void> tapButtonByText(
WidgetTester tester,
String buttonText,
) async {
final buttonFinder = find.widgetWithText(ElevatedButton, buttonText);
if (buttonFinder.evaluate().isEmpty) {
// ElevatedButton이 아닌 경우 다른 버튼 타입 시도
final textButtonFinder = find.widgetWithText(TextButton, buttonText);
if (textButtonFinder.evaluate().isNotEmpty) {
await tester.tap(textButtonFinder);
return;
}
final outlinedButtonFinder = find.widgetWithText(OutlinedButton, buttonText);
if (outlinedButtonFinder.evaluate().isNotEmpty) {
await tester.tap(outlinedButtonFinder);
return;
}
// 아무 버튼도 찾지 못한 경우 텍스트만으로 시도
await tester.tap(find.text(buttonText));
} else {
await tester.tap(buttonFinder);
}
}
/// 스낵바 메시지 검증 헬퍼
void expectSnackBar(WidgetTester tester, String message) {
expect(
find.descendant(
of: find.byType(SnackBar),
matching: find.text(message),
),
findsOneWidget,
);
}
/// 로딩 인디케이터 검증 헬퍼
void expectLoading(WidgetTester tester, {bool isLoading = true}) {
expect(
find.byType(CircularProgressIndicator),
isLoading ? findsOneWidget : findsNothing,
);
}
/// 에러 메시지 검증 헬퍼
void expectErrorMessage(WidgetTester tester, String errorMessage) {
expect(find.text(errorMessage), findsOneWidget);
}
/// 화면 전환 대기 헬퍼
Future<void> waitForNavigation(WidgetTester tester) async {
await tester.pump();
await tester.pump(const Duration(milliseconds: 300)); // 애니메이션 대기
}
/// 다이얼로그 검증 헬퍼
void expectDialog(WidgetTester tester, {String? title, String? content}) {
expect(find.byType(Dialog), findsOneWidget);
if (title != null) {
expect(
find.descendant(
of: find.byType(Dialog),
matching: find.text(title),
),
findsOneWidget,
);
}
if (content != null) {
expect(
find.descendant(
of: find.byType(Dialog),
matching: find.text(content),
),
findsOneWidget,
);
}
}
/// 다이얼로그 닫기 헬퍼
Future<void> closeDialog(WidgetTester tester) async {
// 다이얼로그 외부 탭하여 닫기
await tester.tapAt(const Offset(10, 10));
await tester.pump();
}
/// 스크롤하여 위젯 찾기 헬퍼
Future<void> scrollUntilVisible(
WidgetTester tester,
Finder finder, {
double delta = 300,
int maxScrolls = 10,
Finder? scrollable,
}) async {
final scrollableFinder = scrollable ?? find.byType(Scrollable).first;
for (int i = 0; i < maxScrolls; i++) {
if (finder.evaluate().isNotEmpty) {
return;
}
await tester.drag(scrollableFinder, Offset(0, -delta));
await tester.pump();
}
}
/// 테이블이나 리스트에서 특정 행 찾기 헬퍼
Finder findRowContaining(String text) {
return find.ancestor(
of: find.text(text),
matching: find.byType(Row),
);
}
/// 폼 필드 검증 헬퍼
void expectFormFieldError(WidgetTester tester, String fieldLabel, String errorText) {
final formField = find.ancestor(
of: find.text(fieldLabel),
matching: find.byType(TextFormField),
);
final errorFinder = find.descendant(
of: formField,
matching: find.text(errorText),
);
expect(errorFinder, findsOneWidget);
}
/// 드롭다운 선택 헬퍼
Future<void> selectDropdownItem(
WidgetTester tester,
String dropdownLabel,
String itemText,
) async {
// 드롭다운 찾기
final dropdown = find.ancestor(
of: find.text(dropdownLabel),
matching: find.byType(DropdownButtonFormField),
);
// 드롭다운 열기
await tester.tap(dropdown);
await tester.pump();
// 아이템 선택
await tester.tap(find.text(itemText).last);
await tester.pump();
}
/// 날짜 선택 헬퍼
Future<void> selectDate(
WidgetTester tester,
String dateFieldLabel,
DateTime date,
) async {
// 날짜 필드 탭
final dateField = find.ancestor(
of: find.text(dateFieldLabel),
matching: find.byType(TextFormField),
);
await tester.tap(dateField);
await tester.pump();
// 날짜 선택 (간단한 구현, 실제로는 더 복잡할 수 있음)
await tester.tap(find.text(date.day.toString()));
await tester.pump();
// 확인 버튼 탭
await tester.tap(find.text('확인'));
await tester.pump();
}