Files
lunchpick/test/unit/presentation/providers/restaurant_provider_test.dart
2025-11-26 19:16:27 +09:00

330 lines
9.3 KiB
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/domain/repositories/restaurant_repository.dart';
import 'package:lunchpick/presentation/providers/restaurant_provider.dart';
import 'package:lunchpick/presentation/providers/di_providers.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
@GenerateMocks([RestaurantRepository])
import 'restaurant_provider_test.mocks.dart';
void main() {
group('RestaurantProvider Tests', () {
late ProviderContainer container;
late MockRestaurantRepository mockRepository;
setUp(() {
mockRepository = MockRestaurantRepository();
container = ProviderContainer(
overrides: [
restaurantRepositoryProvider.overrideWithValue(mockRepository),
],
);
});
tearDown(() {
container.dispose();
});
test('restaurantListProvider returns stream of restaurants', () async {
// Arrange
final restaurants = [
Restaurant(
id: '1',
name: '테스트 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
];
when(
mockRepository.watchRestaurants(),
).thenAnswer((_) => Stream.value(restaurants));
// Act
final result = container.read(restaurantListProvider);
// Assert
expect(result, isA<AsyncValue<List<Restaurant>>>());
});
test('searchRestaurantsProvider filters restaurants by query', () async {
// Arrange
final restaurants = [
Restaurant(
id: '1',
name: '김치찌개',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
Restaurant(
id: '2',
name: '된장찌개',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
];
when(
mockRepository.searchRestaurants('김치'),
).thenAnswer((_) async => [restaurants[0]]);
// Act
final result = await container.read(
searchRestaurantsProvider('김치').future,
);
// Assert
expect(result.length, 1);
expect(result.first.name, '김치찌개');
});
test('selectedCategoryProvider updates category filter', () {
// Act
container.read(selectedCategoryProvider.notifier).state = '한식';
// Assert
expect(container.read(selectedCategoryProvider), '한식');
});
test('restaurantNotifier adds new restaurant', () async {
// Arrange
when(mockRepository.addRestaurant(any)).thenAnswer((_) async {});
// Act
final notifier = container.read(restaurantNotifierProvider.notifier);
await notifier.addRestaurant(
name: '새 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시 강남구',
jibunAddress: '서울시 강남구',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
);
// Assert
verify(mockRepository.addRestaurant(any)).called(1);
});
test('restaurantNotifier updates existing restaurant', () async {
// Arrange
final restaurant = Restaurant(
id: '1',
name: '수정할 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
when(mockRepository.updateRestaurant(any)).thenAnswer((_) async {});
// Act
final notifier = container.read(restaurantNotifierProvider.notifier);
await notifier.updateRestaurant(restaurant);
// Assert
verify(mockRepository.updateRestaurant(any)).called(1);
});
test('restaurantNotifier deletes restaurant', () async {
// Arrange
when(mockRepository.deleteRestaurant('1')).thenAnswer((_) async {});
// Act
final notifier = container.read(restaurantNotifierProvider.notifier);
await notifier.deleteRestaurant('1');
// Assert
verify(mockRepository.deleteRestaurant('1')).called(1);
});
test(
'filteredRestaurantsProvider filters by search and category',
() async {
// Arrange
final restaurants = [
Restaurant(
id: '1',
name: '김치찌개',
category: '한식',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
Restaurant(
id: '2',
name: '스시',
category: '일식',
subCategory: '일식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5,
longitude: 127.0,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
];
when(
mockRepository.watchRestaurants(),
).thenAnswer((_) => Stream.value(restaurants));
// Act - 카테고리 필터 설정
container.read(selectedCategoryProvider.notifier).state = '한식';
container.read(searchQueryProvider.notifier).state = '김치';
final filtered = await container.read(
filteredRestaurantsProvider.future,
);
// Assert
expect(filtered.length, 1);
expect(filtered.first.name, '김치찌개');
},
);
test(
'restaurantsWithinDistanceProvider returns nearby restaurants',
() async {
// Arrange
final nearbyRestaurants = [
Restaurant(
id: '1',
name: '가까운 맛집',
category: 'korean',
subCategory: '한식',
roadAddress: '서울시',
jibunAddress: '서울시',
latitude: 37.5665,
longitude: 126.9780,
source: DataSource.USER_INPUT,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
),
];
when(
mockRepository.getRestaurantsWithinDistance(
userLatitude: 37.5665,
userLongitude: 126.9780,
maxDistanceInMeters: 1000,
),
).thenAnswer((_) async => nearbyRestaurants);
// Act
final result = await container.read(
restaurantsWithinDistanceProvider((
latitude: 37.5665,
longitude: 126.9780,
maxDistance: 1000,
)).future,
);
// Assert
expect(result.length, 1);
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();
});
});
}