330 lines
9.4 KiB
Dart
330 lines
9.4 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 = '김치';
|
|
await container.read(restaurantListProvider.future);
|
|
final filteredAsync = container.read(filteredRestaurantsProvider);
|
|
final filtered = filteredAsync.requireValue;
|
|
|
|
// 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();
|
|
});
|
|
});
|
|
}
|