전역 구조 리팩터링 및 테스트 확장

This commit is contained in:
JiWoong Sul
2025-09-29 01:51:47 +09:00
parent c00c0c9ab2
commit fef7108479
70 changed files with 7709 additions and 3185 deletions

View File

@@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:superport_v2/features/util/postal_search/presentation/models/postal_search_result.dart';
import 'package:superport_v2/features/util/postal_search/presentation/widgets/postal_search_dialog.dart';
class _PostalSearchHarness extends StatefulWidget {
const _PostalSearchHarness({required this.fetcher, this.initialKeyword});
final PostalSearchFetcher fetcher;
final String? initialKeyword;
@override
State<_PostalSearchHarness> createState() => _PostalSearchHarnessState();
}
class _PostalSearchHarnessState extends State<_PostalSearchHarness> {
PostalSearchResult? _selection;
bool _dialogScheduled = false;
void _ensureDialogShown(BuildContext innerContext) {
if (_dialogScheduled) {
return;
}
_dialogScheduled = true;
WidgetsBinding.instance.addPostFrameCallback((_) async {
final result = await showPostalSearchDialog(
innerContext,
fetcher: widget.fetcher,
initialKeyword: widget.initialKeyword,
);
if (!mounted) {
return;
}
setState(() {
_selection = result;
});
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (innerContext) {
return ShadTheme(
data: ShadThemeData(
colorScheme: const ShadSlateColorScheme.light(),
brightness: Brightness.light,
),
child: Builder(
builder: (themeContext) {
_ensureDialogShown(themeContext);
return Scaffold(
body: Center(
child: Text(
_selection?.zipcode ?? '선택 없음',
key: const Key('selected_zipcode'),
),
),
);
},
),
);
},
),
);
}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
testWidgets('검색 전 안내 문구를 노출하고 fetcher를 호출하지 않는다', (tester) async {
var called = false;
await tester.pumpWidget(
_PostalSearchHarness(
fetcher: (keyword) async {
called = true;
return [];
},
),
);
await tester.pumpAndSettle();
expect(find.text('검색어를 입력한 뒤 엔터 또는 검색 버튼을 눌러 주세요.'), findsOneWidget);
expect(called, isFalse);
});
testWidgets('검색 실행 후 결과를 선택하면 선택 정보가 반환된다', (tester) async {
var receivedKeyword = '';
await tester.pumpWidget(
_PostalSearchHarness(
fetcher: (keyword) async {
receivedKeyword = keyword;
return [
PostalSearchResult(
zipcode: '06000',
sido: '서울특별시',
sigungu: '강남구',
roadName: '언주로',
buildingNumber: '100',
),
];
},
),
);
await tester.pumpAndSettle();
final inputFinder = find.byType(EditableText);
expect(inputFinder, findsOneWidget);
await tester.enterText(inputFinder, '언주로');
await tester.tap(find.text('검색'));
await tester.pumpAndSettle();
expect(receivedKeyword, '언주로');
expect(find.text('검색 결과 1건'), findsOneWidget);
await tester.tap(find.text('06000'));
await tester.pumpAndSettle();
final selectedFinder = find.byKey(const Key('selected_zipcode'));
expect(selectedFinder, findsOneWidget);
final selectedText = tester.widget<Text>(selectedFinder);
expect(selectedText.data, '06000');
});
testWidgets('initialKeyword가 주어지면 자동으로 검색을 수행한다', (tester) async {
String? receivedKeyword;
await tester.pumpWidget(
_PostalSearchHarness(
fetcher: (keyword) async {
receivedKeyword = keyword;
return [];
},
initialKeyword: '06236',
),
);
await tester.pumpAndSettle();
expect(receivedKeyword, '06236');
await tester.tap(find.text('닫기'));
await tester.pumpAndSettle();
});
}