test(app): add geocoding and restaurant card coverage
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
// ignore_for_file: depend_on_referenced_packages
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:lunchpick/data/datasources/remote/naver_search_service.dart';
|
import 'package:lunchpick/data/datasources/remote/naver_search_service.dart';
|
||||||
import 'package:lunchpick/data/datasources/remote/naver_map_parser.dart';
|
import 'package:lunchpick/data/datasources/remote/naver_map_parser.dart';
|
||||||
@@ -5,6 +9,15 @@ import 'package:lunchpick/data/api/naver_api_client.dart';
|
|||||||
import 'package:lunchpick/data/api/naver/naver_local_search_api.dart';
|
import 'package:lunchpick/data/api/naver/naver_local_search_api.dart';
|
||||||
import 'package:lunchpick/domain/entities/restaurant.dart';
|
import 'package:lunchpick/domain/entities/restaurant.dart';
|
||||||
import 'package:lunchpick/core/errors/network_exceptions.dart';
|
import 'package:lunchpick/core/errors/network_exceptions.dart';
|
||||||
|
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
|
||||||
|
|
||||||
|
class _FakePathProvider extends PathProviderPlatform {
|
||||||
|
@override
|
||||||
|
Future<String?> getTemporaryPath() async {
|
||||||
|
final tempDir = await Directory.systemTemp.createTemp('naver_search_test_');
|
||||||
|
return tempDir.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Mock 클래스들
|
// Mock 클래스들
|
||||||
class MockNaverApiClient extends NaverApiClient {
|
class MockNaverApiClient extends NaverApiClient {
|
||||||
@@ -75,6 +88,9 @@ class MockNaverApiClient extends NaverApiClient {
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
disposeCalled = true;
|
disposeCalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> resolveShortUrl(String shortUrl) async => shortUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockNaverMapParser extends NaverMapParser {
|
class MockNaverMapParser extends NaverMapParser {
|
||||||
@@ -123,6 +139,11 @@ void main() {
|
|||||||
late MockNaverApiClient mockApiClient;
|
late MockNaverApiClient mockApiClient;
|
||||||
late MockNaverMapParser mockMapParser;
|
late MockNaverMapParser mockMapParser;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
PathProviderPlatform.instance = _FakePathProvider();
|
||||||
|
});
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
mockApiClient = MockNaverApiClient();
|
mockApiClient = MockNaverApiClient();
|
||||||
mockMapParser = MockNaverMapParser();
|
mockMapParser = MockNaverMapParser();
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ void main() {
|
|||||||
Restaurant(
|
Restaurant(
|
||||||
id: '1',
|
id: '1',
|
||||||
name: '김치찌개',
|
name: '김치찌개',
|
||||||
category: 'korean',
|
category: '한식',
|
||||||
subCategory: '한식',
|
subCategory: '한식',
|
||||||
roadAddress: '서울시',
|
roadAddress: '서울시',
|
||||||
jibunAddress: '서울시',
|
jibunAddress: '서울시',
|
||||||
@@ -190,7 +190,7 @@ void main() {
|
|||||||
Restaurant(
|
Restaurant(
|
||||||
id: '2',
|
id: '2',
|
||||||
name: '스시',
|
name: '스시',
|
||||||
category: 'japanese',
|
category: '일식',
|
||||||
subCategory: '일식',
|
subCategory: '일식',
|
||||||
roadAddress: '서울시',
|
roadAddress: '서울시',
|
||||||
jibunAddress: '서울시',
|
jibunAddress: '서울시',
|
||||||
@@ -208,10 +208,14 @@ void main() {
|
|||||||
|
|
||||||
// Act - 카테고리 필터 설정
|
// Act - 카테고리 필터 설정
|
||||||
container.read(selectedCategoryProvider.notifier).state = '한식';
|
container.read(selectedCategoryProvider.notifier).state = '한식';
|
||||||
|
container.read(searchQueryProvider.notifier).state = '김치';
|
||||||
|
final filtered = await container.read(
|
||||||
|
filteredRestaurantsProvider.future,
|
||||||
|
);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// filteredRestaurantsProvider는 StreamProvider이므로 실제 테스트에서는
|
expect(filtered.length, 1);
|
||||||
// 비동기 처리가 필요함
|
expect(filtered.first.name, '김치찌개');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -257,5 +261,69 @@ void main() {
|
|||||||
expect(result.first.name, '가까운 맛집');
|
expect(result.first.name, '가까운 맛집');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('categoriesProvider emits sorted unique categories', () async {
|
||||||
|
// Arrange
|
||||||
|
final restaurants = [
|
||||||
|
Restaurant(
|
||||||
|
id: '1',
|
||||||
|
name: '카테고리1',
|
||||||
|
category: '양식',
|
||||||
|
subCategory: '파스타',
|
||||||
|
roadAddress: '서울시',
|
||||||
|
jibunAddress: '서울시',
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
source: DataSource.USER_INPUT,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
Restaurant(
|
||||||
|
id: '2',
|
||||||
|
name: '카테고리2',
|
||||||
|
category: '한식',
|
||||||
|
subCategory: '찌개',
|
||||||
|
roadAddress: '서울시',
|
||||||
|
jibunAddress: '서울시',
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
source: DataSource.USER_INPUT,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
Restaurant(
|
||||||
|
id: '3',
|
||||||
|
name: '카테고리3',
|
||||||
|
category: '한식',
|
||||||
|
subCategory: '비빔밥',
|
||||||
|
roadAddress: '서울시',
|
||||||
|
jibunAddress: '서울시',
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
source: DataSource.USER_INPUT,
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
when(
|
||||||
|
mockRepository.watchRestaurants(),
|
||||||
|
).thenAnswer((_) => Stream.value(restaurants));
|
||||||
|
|
||||||
|
final categoriesContainer = ProviderContainer(
|
||||||
|
overrides: [
|
||||||
|
restaurantListProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(restaurants),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
final categories = await categoriesContainer
|
||||||
|
.read(categoriesProvider.stream)
|
||||||
|
.first;
|
||||||
|
|
||||||
|
expect(categories, ['양식', '한식']);
|
||||||
|
categoriesContainer.dispose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
65
test/widget/restaurant_card_test.dart
Normal file
65
test/widget/restaurant_card_test.dart
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:lunchpick/domain/entities/restaurant.dart';
|
||||||
|
import 'package:lunchpick/presentation/pages/restaurant_list/widgets/restaurant_card.dart';
|
||||||
|
import 'package:lunchpick/presentation/providers/visit_provider.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Restaurant buildRestaurant({required bool needsAddressVerification}) {
|
||||||
|
return Restaurant(
|
||||||
|
id: '1',
|
||||||
|
name: '테스트 식당',
|
||||||
|
category: '한식',
|
||||||
|
subCategory: '찌개',
|
||||||
|
roadAddress: '서울시 중구',
|
||||||
|
jibunAddress: '서울시 중구',
|
||||||
|
latitude: 37.5665,
|
||||||
|
longitude: 126.9780,
|
||||||
|
source: DataSource.USER_INPUT,
|
||||||
|
createdAt: DateTime(2024, 1, 1),
|
||||||
|
updatedAt: DateTime(2024, 1, 1),
|
||||||
|
needsAddressVerification: needsAddressVerification,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProviderScope wrapWithProviders(Widget child) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
lastVisitDateProvider.overrideWithProvider(
|
||||||
|
(id) => FutureProvider((ref) async => null),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: MaterialApp(home: child),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
testWidgets('주소 확인 배지가 표시되고 거리 뱃지가 노출된다', (tester) async {
|
||||||
|
final restaurant = buildRestaurant(needsAddressVerification: true);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrapWithProviders(
|
||||||
|
RestaurantCard(restaurant: restaurant, distanceKm: 2.3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('주소확인'), findsOneWidget);
|
||||||
|
expect(find.text('2.0km 이상'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('주소 확인 플래그가 없으면 배지가 숨겨진다', (tester) async {
|
||||||
|
final restaurant = buildRestaurant(needsAddressVerification: false);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
wrapWithProviders(
|
||||||
|
RestaurantCard(restaurant: restaurant, distanceKm: null),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text('주소확인'), findsNothing);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user