feat(tests): test coverage is 69.2

This commit is contained in:
misha
2026-01-21 09:19:41 +03:00
parent 775d4fb80f
commit f6809a06b3
16 changed files with 5274 additions and 333 deletions

View File

@@ -18,6 +18,8 @@ import 'diets_cubit_test.mocks.dart';
MockSpec<DietRepository>(),
])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('DietsCubit', () {
DietsCubit? dietsCubit;
late MockFeedRepository mockFeedRepository;
@@ -805,6 +807,190 @@ void main() {
expect(dietsCubit!.state.dietsList[0].wasLikedByMe, false);
expect(dietsCubit!.state.dietsList[0].totalLikes, 10);
});
test('_changeDietLikeState - обновляет состояние лайка через stream', () async {
final likesStateSubject = BehaviorSubject<LikesState>();
when(mockDietRepository.dietLikeState).thenAnswer((_) => likesStateSubject.stream);
when(mockDietRepository.dietFavoriteState).thenAnswer((_) => BehaviorSubject<FavoriteState>());
when(mockDietRepository.dietCommentsState).thenAnswer((_) => BehaviorSubject<CommentsState>());
dietsCubit = DietsCubit(
feedRepository: mockFeedRepository,
dietsRepository: mockDietRepository,
);
const testDiet = Diet(
id: 1,
slug: 'test-diet',
name: 'Test Diet',
image: 'https://example.com/image.jpg',
duration: 7,
state: DietState.active,
type: DietType(veg: true, light: false, carb: false, meat: false),
details: 'Test details',
dailySchedule: {},
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
wasLikedByMe: false,
isFavorite: false,
dailyNutrition: [],
);
const dietsPagination = DietsPagination(
data: [testDiet],
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
);
when(mockFeedRepository.getDiets(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
sorting: anyNamed('sorting'),
)).thenAnswer((_) async => dietsPagination);
await dietsCubit!.loadDiets();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние лайка
likesStateSubject.add(const LikesState(wasLikedByMe: true, totalLikes: 11, index: 0));
await Future.delayed(const Duration(milliseconds: 100));
expect(dietsCubit!.state.dietsList[0].wasLikedByMe, true);
expect(dietsCubit!.state.dietsList[0].totalLikes, 11);
await likesStateSubject.close();
});
test('_changeDietFavoriteState - обновляет состояние избранного через stream', () async {
final favoriteStateSubject = BehaviorSubject<FavoriteState>();
when(mockDietRepository.dietLikeState).thenAnswer((_) => BehaviorSubject<LikesState>());
when(mockDietRepository.dietFavoriteState).thenAnswer((_) => favoriteStateSubject.stream);
when(mockDietRepository.dietCommentsState).thenAnswer((_) => BehaviorSubject<CommentsState>());
dietsCubit = DietsCubit(
feedRepository: mockFeedRepository,
dietsRepository: mockDietRepository,
);
const testDiet = Diet(
id: 1,
slug: 'test-diet',
name: 'Test Diet',
image: 'https://example.com/image.jpg',
duration: 7,
state: DietState.active,
type: DietType(veg: true, light: false, carb: false, meat: false),
details: 'Test details',
dailySchedule: {},
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
wasLikedByMe: false,
isFavorite: false,
dailyNutrition: [],
);
const dietsPagination = DietsPagination(
data: [testDiet],
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
);
when(mockFeedRepository.getDiets(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
sorting: anyNamed('sorting'),
)).thenAnswer((_) async => dietsPagination);
await dietsCubit!.loadDiets();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние избранного
favoriteStateSubject.add(const FavoriteState(isFavorite: true, index: 0));
await Future.delayed(const Duration(milliseconds: 100));
expect(dietsCubit!.state.dietsList[0].isFavorite, true);
await favoriteStateSubject.close();
});
test('_changeDietCommentsState - обновляет состояние комментариев через stream', () async {
final commentsStateSubject = BehaviorSubject<CommentsState>();
when(mockDietRepository.dietLikeState).thenAnswer((_) => BehaviorSubject<LikesState>());
when(mockDietRepository.dietFavoriteState).thenAnswer((_) => BehaviorSubject<FavoriteState>());
when(mockDietRepository.dietCommentsState).thenAnswer((_) => commentsStateSubject.stream);
dietsCubit = DietsCubit(
feedRepository: mockFeedRepository,
dietsRepository: mockDietRepository,
);
const testDiet = Diet(
id: 1,
slug: 'test-diet',
name: 'Test Diet',
image: 'https://example.com/image.jpg',
duration: 7,
state: DietState.active,
type: DietType(veg: true, light: false, carb: false, meat: false),
details: 'Test details',
dailySchedule: {},
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
wasLikedByMe: false,
isFavorite: false,
dailyNutrition: [],
);
const dietsPagination = DietsPagination(
data: [testDiet],
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
);
when(mockFeedRepository.getDiets(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
sorting: anyNamed('sorting'),
)).thenAnswer((_) async => dietsPagination);
await dietsCubit!.loadDiets();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние комментариев
commentsStateSubject.add(const CommentsState(index: 0, totalComments: 6));
await Future.delayed(const Duration(milliseconds: 100));
expect(dietsCubit!.state.dietsList[0].totalComments, 6);
await commentsStateSubject.close();
});
test('loadNextPage - не загружает если lastPage не инициализирован', () async {
dietsCubit = DietsCubit(
feedRepository: mockFeedRepository,
dietsRepository: mockDietRepository,
);
// loadNextPage проверяет lastPage, который не инициализирован до loadDiets
// Метод выбросит исключение, но мы просто проверяем, что getDiets не вызывается
try {
await dietsCubit!.loadNextPage();
} catch (e) {
// Ожидаем исключение, так как lastPage не инициализирован
expect(e, isNotNull);
}
verifyNever(mockFeedRepository.getDiets(page: anyNamed('page'), query: anyNamed('query'), types: anyNamed('types'), duration: anyNamed('duration'), sorting: anyNamed('sorting')));
});
});
}

View File

@@ -18,6 +18,7 @@ import 'recipes_cubit_test.mocks.dart';
@GenerateNiceMocks([MockSpec<FeedRepository>(), MockSpec<RecipeRepository>(), MockSpec<AboutRecipeRepository>()])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('RecipesCubit', () {
RecipesCubit? recipesCubit;
late MockFeedRepository mockFeedRepository;
@@ -749,5 +750,219 @@ void main() {
expect(recipesCubit!.state.selectedTags.contains(2), isTrue);
expect(recipesCubit!.state.selectedTagsCounter, 1);
});
test('_changeRecipeLikeState - обновляет состояние лайка через stream', () async {
final likesStateSubject = BehaviorSubject<LikesState>();
when(mockAboutRecipeRepository.recipeLikeState).thenAnswer((_) => likesStateSubject.stream);
when(mockAboutRecipeRepository.recipeFavoriteState).thenAnswer((_) => BehaviorSubject<FavoriteState>());
when(mockAboutRecipeRepository.recipeCommentsState).thenAnswer((_) => BehaviorSubject<CommentsState>());
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
const testRecipe = Recipe(
id: 1,
image: 'https://example.com/image.jpg',
state: 'active',
cookingTime: 30,
activeCookingTime: 15,
ingredients: [],
description: 'Test description',
steps: [],
videoUrl: null,
createdAt: null,
updatedAt: null,
title: 'Test Recipe',
type: 'main',
slug: 'test-recipe',
commentsCount: 5,
wasLikedByMe: false,
isFavorite: false,
calories: 300.0,
servings: 4,
likesCount: 10,
viewsCount: 100,
tags: [],
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
);
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
await recipesCubit!.loadRecipesList();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние лайка
likesStateSubject.add(const LikesState(wasLikedByMe: true, totalLikes: 11, index: 0));
await Future.delayed(const Duration(milliseconds: 100));
expect(recipesCubit!.state.recipesList[0].wasLikedByMe, true);
expect(recipesCubit!.state.recipesList[0].likesCount, 11);
await likesStateSubject.close();
});
test('_changeRecipeFavoriteState - обновляет состояние избранного через stream', () async {
final favoriteStateSubject = BehaviorSubject<FavoriteState>();
when(mockAboutRecipeRepository.recipeLikeState).thenAnswer((_) => BehaviorSubject<LikesState>());
when(mockAboutRecipeRepository.recipeFavoriteState).thenAnswer((_) => favoriteStateSubject.stream);
when(mockAboutRecipeRepository.recipeCommentsState).thenAnswer((_) => BehaviorSubject<CommentsState>());
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
const testRecipe = Recipe(
id: 1,
image: 'https://example.com/image.jpg',
state: 'active',
cookingTime: 30,
activeCookingTime: 15,
ingredients: [],
description: 'Test description',
steps: [],
videoUrl: null,
createdAt: null,
updatedAt: null,
title: 'Test Recipe',
type: 'main',
slug: 'test-recipe',
commentsCount: 5,
wasLikedByMe: false,
isFavorite: false,
calories: 300.0,
servings: 4,
likesCount: 10,
viewsCount: 100,
tags: [],
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
);
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
await recipesCubit!.loadRecipesList();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние избранного
favoriteStateSubject.add(const FavoriteState(isFavorite: true, index: 0));
await Future.delayed(const Duration(milliseconds: 100));
expect(recipesCubit!.state.recipesList[0].isFavorite, true);
await favoriteStateSubject.close();
});
test('_changeRecipeCommentsState - обновляет состояние комментариев через stream', () async {
final commentsStateSubject = BehaviorSubject<CommentsState>();
when(mockAboutRecipeRepository.recipeLikeState).thenAnswer((_) => BehaviorSubject<LikesState>());
when(mockAboutRecipeRepository.recipeFavoriteState).thenAnswer((_) => BehaviorSubject<FavoriteState>());
when(mockAboutRecipeRepository.recipeCommentsState).thenAnswer((_) => commentsStateSubject.stream);
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
const testRecipe = Recipe(
id: 1,
image: 'https://example.com/image.jpg',
state: 'active',
cookingTime: 30,
activeCookingTime: 15,
ingredients: [],
description: 'Test description',
steps: [],
videoUrl: null,
createdAt: null,
updatedAt: null,
title: 'Test Recipe',
type: 'main',
slug: 'test-recipe',
commentsCount: 5,
wasLikedByMe: false,
isFavorite: false,
calories: 300.0,
servings: 4,
likesCount: 10,
viewsCount: 100,
tags: [],
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
);
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
await recipesCubit!.loadRecipesList();
await Future.delayed(const Duration(milliseconds: 200));
// Эмитим новое состояние комментариев
commentsStateSubject.add(const CommentsState(index: 0, totalComments: 6));
await Future.delayed(const Duration(milliseconds: 100));
expect(recipesCubit!.state.recipesList[0].commentsCount, 6);
await commentsStateSubject.close();
});
test('deleteRecipe - удаляет рецепт', () async {
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
when(mockAboutRecipeRepository.deleteRecipe('test-recipe')).thenAnswer((_) async => Future.value());
final result = await recipesCubit!.deleteRecipe('test-recipe');
expect(result, true);
verify(mockAboutRecipeRepository.deleteRecipe('test-recipe')).called(1);
});
test('deleteRecipe - возвращает false при ошибке', () async {
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
when(mockAboutRecipeRepository.deleteRecipe('test-recipe')).thenThrow(Exception('Ошибка удаления'));
final result = await recipesCubit!.deleteRecipe('test-recipe');
expect(result, false);
});
test('loadNextPage - не загружает если lastPage не инициализирован', () async {
recipesCubit = RecipesCubit(
feedRepository: mockFeedRepository,
recipeRepository: mockRecipeRepository,
aboutRecipeRepository: mockAboutRecipeRepository,
);
// loadNextPage проверяет lastPage, который не инициализирован до loadRecipesList
// Метод выбросит исключение, но мы просто проверяем, что getRecipes не вызывается
try {
await recipesCubit!.loadNextPage();
} catch (e) {
// Ожидаем исключение, так как lastPage не инициализирован
expect(e, isNotNull);
}
verifyNever(mockFeedRepository.getRecipes(page: anyNamed('page'), tags: anyNamed('tags'), query: anyNamed('query')));
});
});
}