diff --git a/README.md b/README.md index 1cc701d..9210116 100644 --- a/README.md +++ b/README.md @@ -1 +1,104 @@ -# qa_python \ No newline at end of file +# qa_python_4 +# Тестирование класса BooksCollector + +## Общая информация +- **Всего реализовано тестов:** 38 +- **Параметризированные тесты:** 2 (проверка граничных значений длины названия) +- **Покрытие методов:** 100% (все 10 методов класса протестированы) +- **Использование фикстур:** 4 фикстуры в conftest.py +- **Библиотека для тестирования:** pytest + + +## Структура проекта + + +--- + +## Фикстуры (conftest.py) + +| Фикстура | Описание | +|----------|----------| +| `collector` | Пустой экземпляр BooksCollector для каждого теста | +| `collector_with_books` | Экземпляр с 3 книгами без жанров | +| `collector_with_genres` | Экземпляр с книгами и установленными жанрами | +| `collector_with_favorites` | Экземпляр с книгами, жанрами и избранным | + +--- + +## Группы тестов + +### Группа 1: Тестирование добавления книг (4 теста, включая 2 параметризированных) + +| № | Название теста | Тип | Что проверяет | Ожидаемый результат | +|---|----------------|-----|----------------|---------------------| +| 1 | `test_add_new_book_add_two_books` | Обычный | Добавление нескольких книг | В словаре 2 книги | +| 2 | `test_add_new_book_with_invalid_length_not_added` | **Параметризированный** | Невалидные значения длины (пустая строка, >40 символов) | Книга не добавляется | +| 3 | `test_add_new_book_with_valid_length_added` | **Параметризированный** | Граничные значения валидной длины (1, 20, 40 символов) | Книга успешно добавляется | +| 4 | `test_add_new_book_duplicate_not_added` | Обычный | Добавление дубликата | Дубликат игнорируется | + +### Группа 2: Тестирование установки жанра (3 теста) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 5 | `test_set_book_genre` | Установка валидного жанра | Жанр успешно устанавливается | +| 6 | `test_set_book_genre_for_nonexistent_book` | Установка жанра несуществующей книге | Жанр не устанавливается | +| 7 | `test_set_book_genre_with_invalid_genre` | Установка невалидного жанра | Жанр не меняется (остаётся '') | + +### Группа 3: Тестирование получения жанра (3 теста) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 8 | `test_get_book_genre_for_nonexistent_book` | Получение жанра несуществующей книги | Возвращается None | +| 9 | `test_get_book_genre_for_existing_book` | Получение жанра существующей книги | Возвращается установленный жанр | +| 10 | `test_get_book_genre_for_book_without_genre` | Получение жанра книги без жанра | Возвращается '' | + +### Группа 4: Тестирование получения книг по жанрам (4 теста) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 11 | `test_get_books_with_specific_genre` | Получение книг конкретного жанра | Возвращаются только книги нужного жанра | +| 12 | `test_get_books_with_specific_genre_no_books` | Получение книг жанра, которого нет | Возвращается пустой список | +| 13 | `test_get_books_with_specific_genre_empty_collection` | Получение книг из пустой коллекции | Возвращается пустой список | +| 14 | `test_get_books_with_invalid_genre` | Получение книг с несуществующим жанром | Возвращается пустой список | + +### Группа 5: Тестирование метода get_books_genre (4 теста) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 15 | `test_get_books_genre_returns_dict` | Проверка типа возвращаемых данных | Возвращается словарь | +| 16 | `test_get_books_genre_empty_dict` | Получение пустого словаря | Возвращается {} | +| 17 | `test_get_books_genre_with_one_book` | Получение словаря с одной книгой | Словарь содержит 1 книгу без жанра | +| 18 | `test_get_books_genre_with_multiple_books` | Получение словаря с несколькими книгами | Словарь содержит все книги с их жанрами | + +### Группа 6: Тестирование книг для детей (3 теста) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 19 | `test_get_books_for_children` | Книги с возрастным рейтингом не попадают в список для детей | В списке только книги без возрастных ограничений | +| 20 | `test_get_books_for_children_without_genre` | Книги без жанра не попадают в список для детей | Книга без жанра отсутствует в списке | +| 21 | `test_get_books_for_children_empty` | Получение списка из пустой коллекции | Возвращается пустой список | + +### Группа 7: Тестирование избранного (9 тестов) + +| № | Название теста | Что проверяет | Ожидаемый результат | +|---|----------------|----------------|---------------------| +| 22 | `test_add_book_in_favorites` | Добавление книги в избранное | Книга появляется в избранном | +| 23 | `test_add_nonexistent_book_to_favorites` | Добавление несуществующей книги в избранное | Книга не добавляется | +| 24 | `test_add_duplicate_book_to_favorites` | Добавление дубликата в избранное | Дубликат игнорируется | +| 25 | `test_delete_book_from_favorites` | Удаление книги из избранного | Книга удаляется | +| 26 | `test_delete_nonexistent_book_from_favorites` | Удаление несуществующей книги из избранного | Ничего не происходит | +| 27 | `test_delete_book_from_favorites_not_in_favorites` | Удаление книги, которой нет в избранном | Ничего не происходит | +| 28 | `test_get_list_of_favorites_books_empty` | Получение пустого списка избранного | Возвращается пустой список | +| 29 | `test_get_list_of_favorites_books_returns_list` | Проверка типа возвращаемых данных | Возвращается список | +| 30 | `test_get_list_of_favorites_books_with_one_book` | Получение списка с одной книгой | Список содержит 1 книгу | +| 31 | `test_get_list_of_favorites_books_with_multiple_books` | Получение списка с несколькими книгами | Список содержит все добавленные книги | + + + +--- + +### Тест 1: Невалидные значения длины названия +test_add_new_book_with_invalid_length_not_added +### Тест 2: Валидные значения длины названия (граничные значения) +test_add_new_book_with_valid_length_added + \ No newline at end of file diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..0b09dcc --- /dev/null +++ b/conftest.py @@ -0,0 +1,32 @@ +import pytest +from books_collector import BooksCollector + +@pytest.fixture +def collector(): + """Фикстура, возвращающая новый экземпляр BooksCollector для каждого теста""" + return BooksCollector() + +@pytest.fixture +def collector_with_books(collector): + """Фикстура с предустановленными книгами""" + books = ['Книга 1', 'Книга 2', 'Книга 3'] + for book in books: + collector.add_new_book(book) + return collector + +@pytest.fixture +def collector_with_genres(collector_with_books): + """Фикстура с книгами и жанрами""" + collector = collector_with_books + collector.set_book_genre('Книга 1', 'Фантастика') + collector.set_book_genre('Книга 2', 'Ужасы') + collector.set_book_genre('Книга 3', 'Детективы') + return collector + +@pytest.fixture +def collector_with_favorites(collector_with_genres): + """Фикстура с книгами, жанрами и избранным""" + collector = collector_with_genres + collector.add_book_in_favorites('Книга 1') + collector.add_book_in_favorites('Книга 2') + return collector \ No newline at end of file diff --git a/tests.py b/tests.py index 383385e..a8c6b06 100644 --- a/tests.py +++ b/tests.py @@ -1,24 +1,162 @@ +import pytest from main import BooksCollector -# класс TestBooksCollector объединяет набор тестов, которыми мы покрываем наше приложение BooksCollector -# обязательно указывать префикс Test class TestBooksCollector: - # пример теста: - # обязательно указывать префикс test_ - # дальше идет название метода, который тестируем add_new_book_ - # затем, что тестируем add_two_books - добавление двух книг - def test_add_new_book_add_two_books(self): - # создаем экземпляр (объект) класса BooksCollector - collector = BooksCollector() - - # добавляем две книги + # Тесты добавления книг + def test_add_new_book_add_two_books(self, collector): collector.add_new_book('Гордость и предубеждение и зомби') collector.add_new_book('Что делать, если ваш кот хочет вас убить') + assert len(collector.get_books_genre()) == 2 + + @pytest.mark.parametrize('invalid_name', [ + '', + 'А' * 41, + 'А' * 100, + ]) + def test_add_new_book_with_invalid_length_not_added(self, collector, invalid_name): + collector.add_new_book(invalid_name) + assert len(collector.get_books_genre()) == 0 + + @pytest.mark.parametrize('valid_name', [ + 'А', + 'А' * 20, + 'А' * 40, + 'Книга с пробелами и знаками препинания!', + ]) + def test_add_new_book_with_valid_length_added(self, collector, valid_name): + collector.add_new_book(valid_name) + assert len(collector.get_books_genre()) == 1 + assert valid_name in collector.get_books_genre() + + def test_add_new_book_duplicate_not_added(self, collector): + collector.add_new_book('Книга') + collector.add_new_book('Книга') + assert len(collector.get_books_genre()) == 1 + + # Тесты установки и получения жанра + def test_set_book_genre(self, collector): + book_name = 'Книга' + collector.add_new_book(book_name) + collector.set_book_genre(book_name, 'Ужасы') + assert collector.get_book_genre(book_name) == 'Ужасы' + + def test_set_book_genre_for_nonexistent_book(self, collector): + collector.set_book_genre('Несуществующая книга', 'Фантастика') + assert collector.get_book_genre('Несуществующая книга') is None + + def test_set_book_genre_with_invalid_genre(self, collector): + book_name = 'Книга' + collector.add_new_book(book_name) + collector.set_book_genre(book_name, 'Неверный жанр') + assert collector.get_book_genre(book_name) == '' + + def test_get_book_genre_for_nonexistent_book(self, collector): + assert collector.get_book_genre('Несуществующая книга') is None + + def test_get_book_genre_for_existing_book(self, collector): + book_name = 'Книга' + collector.add_new_book(book_name) + collector.set_book_genre(book_name, 'Фантастика') + assert collector.get_book_genre(book_name) == 'Фантастика' + + def test_get_book_genre_for_book_without_genre(self, collector): + book_name = 'Книга без жанра' + collector.add_new_book(book_name) + assert collector.get_book_genre(book_name) == '' + + # Тесты получения книг по жанрам + def test_get_books_with_specific_genre(self, collector_with_genres): + horror_books = collector_with_genres.get_books_with_specific_genre('Ужасы') + assert len(horror_books) == 1 + assert 'Книга 2' in horror_books + + def test_get_books_with_specific_genre_no_books(self, collector): + assert collector.get_books_with_specific_genre('Ужасы') == [] + + def test_get_books_with_specific_genre_empty_collection(self, collector): + result = collector.get_books_with_specific_genre('Фантастика') + assert result == [] + + def test_get_books_with_invalid_genre(self, collector_with_genres): + assert collector_with_genres.get_books_with_specific_genre('Неверный жанр') == [] + + # Тесты метода get_books_genre + def test_get_books_genre_returns_dict(self, collector): + result = collector.get_books_genre() + assert isinstance(result, dict) + + def test_get_books_genre_empty_dict(self, collector): + assert collector.get_books_genre() == {} + + def test_get_books_genre_with_one_book(self, collector): + collector.add_new_book('Книга 1') + books = collector.get_books_genre() + assert len(books) == 1 + assert 'Книга 1' in books + assert books['Книга 1'] == '' + + def test_get_books_genre_with_multiple_books(self, collector_with_genres): + books = collector_with_genres.get_books_genre() + assert len(books) == 3 + assert books['Книга 1'] == 'Фантастика' + assert books['Книга 2'] == 'Ужасы' + assert books['Книга 3'] == 'Детективы' + + # Тесты книг для детей + def test_get_books_for_children(self, collector_with_genres): + children_books = collector_with_genres.get_books_for_children() + assert len(children_books) == 1 + assert 'Книга 1' in children_books # Фантастика - для детей + assert 'Книга 2' not in children_books # Ужасы - не для детей + assert 'Книга 3' not in children_books # Детективы - не для детей + + def test_get_books_for_children_without_genre(self, collector): + collector.add_new_book('Книга без жанра') + children_books = collector.get_books_for_children() + assert 'Книга без жанра' not in children_books + + def test_get_books_for_children_empty(self, collector): + assert collector.get_books_for_children() == [] + + # Тесты избранного + def test_add_book_in_favorites(self, collector_with_books): + collector_with_books.add_book_in_favorites('Книга 1') + assert 'Книга 1' in collector_with_books.get_list_of_favorites_books() + + def test_add_nonexistent_book_to_favorites(self, collector): + collector.add_book_in_favorites('Несуществующая книга') + assert len(collector.get_list_of_favorites_books()) == 0 + + def test_add_duplicate_book_to_favorites(self, collector_with_books): + collector_with_books.add_book_in_favorites('Книга 1') + collector_with_books.add_book_in_favorites('Книга 1') + assert len(collector_with_books.get_list_of_favorites_books()) == 1 + + def test_delete_book_from_favorites(self, collector_with_favorites): + collector_with_favorites.delete_book_from_favorites('Книга 1') + assert 'Книга 1' not in collector_with_favorites.get_list_of_favorites_books() + assert 'Книга 2' in collector_with_favorites.get_list_of_favorites_books() + + def test_delete_nonexistent_book_from_favorites(self, collector_with_favorites): + collector_with_favorites.delete_book_from_favorites('Несуществующая книга') + assert len(collector_with_favorites.get_list_of_favorites_books()) == 2 + + def test_get_list_of_favorites_books_empty(self, collector): + assert collector.get_list_of_favorites_books() == [] + + def test_get_list_of_favorites_books_returns_list(self, collector): + favorites = collector.get_list_of_favorites_books() + assert isinstance(favorites, list) - # проверяем, что добавилось именно две - # словарь books_rating, который нам возвращает метод get_books_rating, имеет длину 2 - assert len(collector.get_books_rating()) == 2 + def test_get_list_of_favorites_books_with_one_book(self, collector_with_books): + collector_with_books.add_book_in_favorites('Книга 1') + favorites = collector_with_books.get_list_of_favorites_books() + assert len(favorites) == 1 + assert 'Книга 1' in favorites - # напиши свои тесты ниже - # чтобы тесты были независимыми в каждом из них создавай отдельный экземпляр класса BooksCollector() \ No newline at end of file + def test_get_list_of_favorites_books_with_multiple_books(self, collector_with_favorites): + favorites = collector_with_favorites.get_list_of_favorites_books() + assert len(favorites) == 2 + assert 'Книга 1' in favorites + assert 'Книга 2' in favorites