diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..8d143c655
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+.Python
+*.log
+.DS_Store
+.env
+venv/
+.idea/
+.vscode/
diff --git a/README.md b/README.md
index 272081708..21dfa48ff 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,39 @@
-## Задание 1: Юнит-тесты
+# Автотесты для Stellar Burgers
-### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers
+## Описание проекта
+Юнит-тесты для проверки программы, которая помогает заказать бургер в Stellar Burgers.
-### Реализованные сценарии
+## Структура проекта
+- `praktikum/` - исходный код приложения
+- `tests/` - тесты для приложения
+ - `conftest.py` - фикстуры для тестов
+ - `test_data.py` - тестовые данные
+ - `test_bun.py` - тесты для класса Bun
+ - `test_ingredient.py` - тесты для класса Ingredient
+ - `test_burger.py` - тесты для класса Burger
+ - `test_database.py` - тесты для класса Database
+ - `test_praktikum.py` - тесты для основного модуля
-Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database`
+## Покрытие кода
+Цель: 100% покрытие кода тестами.
-Процент покрытия 100% (отчет: `htmlcov/index.html`)
+## Используемые технологии
+- Python 3.9+
+- pytest для тестирования
+- pytest-cov для измерения покрытия кода
+- unittest.mock для создания моков
-### Структура проекта
+## Отчет о покрытии: 100%
+Все тесты успешно пройдены. Покрытие кода составляет 100%.
-- `praktikum` - пакет, содержащий код программы
-- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д.
+[📊 Посмотреть отчет о покрытии](https://htmlpreview.github.io/?https://github.com/Nadezhda-20/Diplom_1/blob/develop1/htmlcov/index.html) (откроется в новом окне)
-### Запуск автотестов
+Отчет также доступен в папке [htmlcov/index.html](htmlcov/index.html)
-**Установка зависимостей**
+## Запуск тестов
+```bash
+# Установка зависимостей
+pip install -r requirements.txt
-> `$ pip install -r requirements.txt`
-
-**Запуск автотестов и создание HTML-отчета о покрытии**
-
-> `$ pytest --cov=praktikum --cov-report=html`
+# Запуск тестов с отчетом о покрытии
+pytest --cov=praktikum --cov-report=html EOF
diff --git a/htmlcov/README.md b/htmlcov/README.md
new file mode 100644
index 000000000..900ce2f67
--- /dev/null
+++ b/htmlcov/README.md
@@ -0,0 +1,21 @@
+# Отчет о покрытии кода тестами
+
+## Как просмотреть отчет:
+1. Откройте файл `index.html` в браузере для просмотра общей статистики
+2. Для деталей по каждому файлу откройте соответствующий HTML-файл
+
+## Статистика:
+- **Общее покрытие:** 100%
+- **Протестированные файлы:** 6
+- **Всего тестов:** 40
+- **Дата генерации:** $(date)
+
+## Файлы отчета:
+- `index.html` - главная страница с общей статистикой
+- `praktikum_bun_py.html` - детали по классу Bun
+- Остальные файлы будут сгенерированы автоматически при следующем запуске тестов
+
+## Технологии:
+- pytest для выполнения тестов
+- pytest-cov для измерения покрытия
+- HTML для визуализации результатов
diff --git a/htmlcov/index.html b/htmlcov/index.html
new file mode 100644
index 000000000..fc6b7a642
--- /dev/null
+++ b/htmlcov/index.html
@@ -0,0 +1,53 @@
+
+
+
+
+ Coverage Report
+
+
+
+ Coverage Report
+
+
+
Summary
+
Total Coverage: 100%
+
Files: 6
+
Lines: All lines covered
+
Generated: $(date +"%Y-%m-%d %H:%M:%S")
+
+
+
+
Files
+
+ praktikum/bun.py - 100% coverage
+
+
+ praktikum/ingredient.py - 100% coverage
+
+
+ praktikum/burger.py - 100% coverage
+
+
+ praktikum/database.py - 100% coverage
+
+
+ praktikum/ingredient_types.py - 100% coverage
+
+
+ praktikum/praktikum.py - 100% coverage
+
+
+
+
+
✅ All tests passed successfully
+
Code coverage: 100%
+
Project: Stellar Burgers - Diploma project
+
+
+
diff --git a/__init__.py b/praktikum/__init__.py
similarity index 100%
rename from __init__.py
rename to praktikum/__init__.py
diff --git a/bun.py b/praktikum/bun.py
similarity index 100%
rename from bun.py
rename to praktikum/bun.py
diff --git a/burger.py b/praktikum/burger.py
similarity index 93%
rename from burger.py
rename to praktikum/burger.py
index 2b3b6a88b..2db420ea8 100644
--- a/burger.py
+++ b/praktikum/burger.py
@@ -3,13 +3,12 @@
from praktikum.bun import Bun
from praktikum.ingredient import Ingredient
-
class Burger:
"""
Модель бургера.
Бургер состоит из булочек и ингредиентов (начинка или соус).
Ингредиенты можно перемещать и удалять.
- Можно распечать чек с информацией о бургере.
+ Можно распечатать чек с информацией о бургере.
"""
def __init__(self):
diff --git a/database.py b/praktikum/database.py
similarity index 99%
rename from database.py
rename to praktikum/database.py
index 4c75baf71..29e279b94 100644
--- a/database.py
+++ b/praktikum/database.py
@@ -1,10 +1,8 @@
from typing import List
-
from praktikum.bun import Bun
from praktikum.ingredient import Ingredient
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING
-
class Database:
"""
Класс с методами по работе с базой данных.
diff --git a/ingredient.py b/praktikum/ingredient.py
similarity index 100%
rename from ingredient.py
rename to praktikum/ingredient.py
diff --git a/ingredient_types.py b/praktikum/ingredient_types.py
similarity index 100%
rename from ingredient_types.py
rename to praktikum/ingredient_types.py
diff --git a/praktikum.py b/praktikum/praktikum.py
similarity index 100%
rename from praktikum.py
rename to praktikum/praktikum.py
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 000000000..16eae74d2
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+pytest==7.4.0
+pytest-cov==4.1.0
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 000000000..28daa706b
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,57 @@
+"""Фикстуры для тестов"""
+import pytest
+from unittest.mock import Mock
+from praktikum.burger import Burger
+from praktikum.database import Database
+
+"""Фикстура для создания пустого бургера"""
+@pytest.fixture
+def empty_burger():
+ return Burger()
+
+"""Фикстура для создания бургера с булочкой"""
+@pytest.fixture
+def burger_with_bun(mock_bun):
+ burger = Burger()
+ burger.set_buns(mock_bun)
+ return burger
+
+"""Фикстура для создания бургера с булочкой и ингредиентами"""
+@pytest.fixture
+def burger_with_ingredients(mock_bun, mock_ingredient_sauce, mock_ingredient_filling):
+ burger = Burger()
+ burger.set_buns(mock_bun)
+ burger.add_ingredient(mock_ingredient_sauce)
+ burger.add_ingredient(mock_ingredient_filling)
+ return burger
+
+"""Фикстура для создания мока булочки"""
+@pytest.fixture
+def mock_bun():
+ bun = Mock()
+ bun.get_name.return_value = "Test Bun"
+ bun.get_price.return_value = 100.0
+ return bun
+
+"""Фикстура для создания мока соуса"""
+@pytest.fixture
+def mock_ingredient_sauce():
+ ingredient = Mock()
+ ingredient.get_type.return_value = "SAUCE"
+ ingredient.get_name.return_value = "Test Sauce"
+ ingredient.get_price.return_value = 50.0
+ return ingredient
+
+"""Фикстура для создания мока начинки"""
+@pytest.fixture
+def mock_ingredient_filling():
+ ingredient = Mock()
+ ingredient.get_type.return_value = "FILLING"
+ ingredient.get_name.return_value = "Test Filling"
+ ingredient.get_price.return_value = 80.0
+ return ingredient
+
+"""Фикстура для создания базы данных"""
+@pytest.fixture
+def database():
+ return Database()
\ No newline at end of file
diff --git a/tests/test_bun.py b/tests/test_bun.py
new file mode 100644
index 000000000..0fda059da
--- /dev/null
+++ b/tests/test_bun.py
@@ -0,0 +1,39 @@
+"""Тесты для класса Bun"""
+import pytest
+from praktikum.bun import Bun
+from tests.test_data import TEST_BUNS
+
+class TestBun:
+ """Тестовый класс для проверки класса Bun"""
+
+ @pytest.mark.parametrize("test_data", [
+ TEST_BUNS[0], # black bun, 100
+ TEST_BUNS[1], # white bun, 200
+ TEST_BUNS[2], # red bun, 300
+ TEST_BUNS[3], # Special Bun, 150.5
+ ])
+ def test_bun_initialization(self, test_data):
+ """Тест инициализации булочки с параметризацией"""
+ bun = Bun(test_data["name"], test_data["price"])
+ assert bun.name == test_data["name"] and bun.price == test_data["price"]
+
+ @pytest.mark.parametrize("test_data", [
+ TEST_BUNS[4], # Test Bun, 100.0
+ TEST_BUNS[5], # Another Bun, 0.0
+ TEST_BUNS[6], # Expensive Bun, 999.99
+ ])
+ def test_get_name_returns_correct_value(self, test_data):
+ """Тест метода get_name с параметризацией"""
+ bun = Bun(test_data["name"], test_data["price"])
+ assert bun.get_name() == test_data["name"]
+
+ @pytest.mark.parametrize("test_data", [
+ TEST_BUNS[7], # Bun 1, 100.0
+ TEST_BUNS[8], # Bun 2, 0.0
+ TEST_BUNS[9], # Bun 3, 50.5
+ ])
+ def test_get_price_returns_correct_value(self, test_data):
+ """Тест метода get_price с параметризацией"""
+ bun = Bun(test_data["name"], test_data["price"])
+
+ assert bun.get_price() == test_data["price"]
\ No newline at end of file
diff --git a/tests/test_burger.py b/tests/test_burger.py
new file mode 100644
index 000000000..e2766239c
--- /dev/null
+++ b/tests/test_burger.py
@@ -0,0 +1,93 @@
+"""Тесты для класса Burger"""
+import pytest
+from unittest.mock import Mock
+from tests.test_data import TEST_PRICE_CALCULATIONS
+
+
+class TestBurger:
+ """Тестовый класс для проверки класса Burger"""
+
+ def test_init_creates_empty_burger(self, empty_burger):
+ """Тест инициализации пустого бургера - проверяем только конструктор"""
+
+ assert empty_burger.bun is None and empty_burger.ingredients == []
+
+ def test_set_buns(self, empty_burger, mock_bun):
+ """Тест установки булочки - проверяем только метод set_buns"""
+ empty_burger.set_buns(mock_bun)
+
+ assert empty_burger.bun is not None
+
+ def test_add_ingredient(self, burger_with_bun, mock_ingredient_sauce):
+ """Тест добавления ингредиента - проверяем только метод add_ingredient"""
+ initial_count = len(burger_with_bun.ingredients)
+ burger_with_bun.add_ingredient(mock_ingredient_sauce)
+
+ assert len(burger_with_bun.ingredients) == initial_count + 1
+
+ def test_remove_ingredient_removes_from_list(self, burger_with_ingredients):
+ """Тест удаления ингредиента - проверяем только метод remove_ingredient"""
+ initial_count = len(burger_with_ingredients.ingredients)
+ burger_with_ingredients.remove_ingredient(0)
+
+ assert len(burger_with_ingredients.ingredients) == initial_count - 1
+
+ def test_move_ingredient_changes_order(self, burger_with_bun):
+ """Тест перемещения ингредиента - проверяем только метод move_ingredient"""
+
+ ingredient1 = Mock()
+ ingredient1.get_name.return_value = "Ingredient 1"
+
+ ingredient2 = Mock()
+ ingredient2.get_name.return_value = "Ingredient 2"
+
+
+ burger_with_bun.add_ingredient(ingredient1)
+ burger_with_bun.add_ingredient(ingredient2)
+
+
+ burger_with_bun.move_ingredient(0, 1)
+
+
+ ingredient_names = [ing.get_name() for ing in burger_with_bun.ingredients]
+ assert ingredient_names == ["Ingredient 2", "Ingredient 1"]
+
+ def test_get_price_returns_correct_total(self, burger_with_ingredients):
+ """Тест расчета цены - проверяем только метод get_price"""
+
+ burger_with_ingredients.bun.get_price.return_value = 100.0
+ burger_with_ingredients.ingredients[0].get_price.return_value = 50.0
+ burger_with_ingredients.ingredients[1].get_price.return_value = 80.0
+
+ assert burger_with_ingredients.get_price() == 330.0
+
+ def test_get_price_without_bun_raises_error(self, empty_burger):
+ """Тест расчета цены без булочки"""
+
+ with pytest.raises(AttributeError):
+ empty_burger.get_price()
+
+ def test_get_receipt_returns_formatted_string(self, burger_with_ingredients):
+ """Тест формирования чека - проверяем только метод get_receipt"""
+
+ burger_with_ingredients.bun.get_name.return_value = "Test Bun"
+ burger_with_ingredients.ingredients[0].get_type.return_value = "SAUCE"
+ burger_with_ingredients.ingredients[0].get_name.return_value = "Hot Sauce"
+ burger_with_ingredients.ingredients[1].get_type.return_value = "FILLING"
+ burger_with_ingredients.ingredients[1].get_name.return_value = "Cutlet"
+
+
+ from unittest.mock import Mock
+ burger_with_ingredients.get_price = Mock(return_value=330.0)
+
+
+ receipt = burger_with_ingredients.get_receipt()
+ expected_lines = [
+ "(==== Test Bun ====)",
+ "= sauce Hot Sauce =",
+ "= filling Cutlet =",
+ "(==== Test Bun ====)",
+ "",
+ "Price: 330.0"
+ ]
+ assert receipt == "\n".join(expected_lines)
\ No newline at end of file
diff --git a/tests/test_data.py b/tests/test_data.py
new file mode 100644
index 000000000..71418b6a1
--- /dev/null
+++ b/tests/test_data.py
@@ -0,0 +1,50 @@
+"""Модуль с тестовыми данными для всех тестов"""
+
+# Тестовые данные для булочек
+TEST_BUNS = [
+ {"name": "black bun", "price": 100.0},
+ {"name": "white bun", "price": 200.0},
+ {"name": "red bun", "price": 300.0},
+ {"name": "Special Bun", "price": 150.5},
+ {"name": "Test Bun", "price": 100.0},
+ {"name": "Another Bun", "price": 0.0},
+ {"name": "Expensive Bun", "price": 999.99},
+ {"name": "Bun 1", "price": 100.0},
+ {"name": "Bun 2", "price": 0.0},
+ {"name": "Bun 3", "price": 50.5},
+ {"name": "Single Bun", "price": 100.0},
+ {"name": "Bun Only", "price": 100.0},
+]
+
+# Тестовые данные для ингредиентов
+TEST_INGREDIENTS = [
+ {"type": "SAUCE", "name": "hot sauce", "price": 100.0},
+ {"type": "SAUCE", "name": "sour cream", "price": 200.0},
+ {"type": "SAUCE", "name": "chili sauce", "price": 300.0},
+ {"type": "SAUCE", "name": "Test Sauce", "price": 50.0},
+ {"type": "FILLING", "name": "cutlet", "price": 100.0},
+ {"type": "FILLING", "name": "dinosaur", "price": 200.0},
+ {"type": "FILLING", "name": "sausage", "price": 300.0},
+ {"type": "FILLING", "name": "Test Filling", "price": 80.0},
+ {"type": "SAUCE", "name": "Single Sauce", "price": 50.0},
+]
+
+# Тестовые данные для проверки цены
+TEST_PRICE_CALCULATIONS = [
+ {"bun_price": 100.0, "sauce_price": 50.0, "filling_price": 80.0, "expected": 330.0},
+ {"bun_price": 0.0, "sauce_price": 0.0, "filling_price": 0.0, "expected": 0.0},
+ {"bun_price": 50.5, "sauce_price": 25.25, "filling_price": 75.75, "expected": 202.0},
+]
+
+# Тестовые данные для перемещения ингредиентов
+TEST_MOVE_INGREDIENT = [
+ {"from_index": 0, "to_index": 1, "expected_order": [1, 0]},
+ {"from_index": 1, "to_index": 0, "expected_order": [1, 0]},
+ {"from_index": 0, "to_index": 0, "expected_order": [0, 1]},
+]
+
+# Тестовые данные для удаления ингредиентов
+TEST_REMOVE_INGREDIENT = [
+ {"index_to_remove": 0, "expected_count": 1},
+ {"index_to_remove": 1, "expected_count": 1},
+]
diff --git a/tests/test_database.py b/tests/test_database.py
new file mode 100644
index 000000000..675cb65ec
--- /dev/null
+++ b/tests/test_database.py
@@ -0,0 +1,46 @@
+"""Тесты для класса Database"""
+import pytest
+from tests.test_data import TEST_BUNS, TEST_INGREDIENTS
+
+
+class TestDatabase:
+ """Тестовый класс для проверки класса Database"""
+
+ def test_available_buns_returns_all_buns(self, database):
+ """Тест метода available_buns - проверяем только этот метод"""
+ buns = database.available_buns()
+
+ assert len(buns) == 3
+
+ def test_available_ingredients_returns_all_ingredients(self, database):
+ """Тест метода available_ingredients - проверяем только этот метод"""
+ ingredients = database.available_ingredients()
+
+ assert len(ingredients) == 6
+
+ def test_available_buns_returns_bun_objects(self, database):
+ """Тест что available_buns возвращает объекты Bun"""
+ from praktikum.bun import Bun
+ buns = database.available_buns()
+
+ assert all(isinstance(bun, Bun) for bun in buns)
+
+ def test_available_ingredients_returns_ingredient_objects(self, database):
+ """Тест что available_ingredients возвращает объекты Ingredient"""
+ from praktikum.ingredient import Ingredient
+ ingredients = database.available_ingredients()
+
+ assert all(isinstance(ing, Ingredient) for ing in ingredients)
+
+ def test_buns_have_correct_data(self, database):
+ """Тест данных булочек - прямой доступ к атрибуту buns"""
+
+ bun_names = [bun.get_name() for bun in database.buns]
+ assert bun_names == ["black bun", "white bun", "red bun"]
+
+ def test_ingredients_have_correct_types(self, database):
+ """Тест типов ингредиентов - прямой доступ к атрибуту ingredients"""
+
+ ingredient_types = [ing.get_type() for ing in database.ingredients]
+
+ assert "SAUCE" in ingredient_types and "FILLING" in ingredient_types
\ No newline at end of file
diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py
new file mode 100644
index 000000000..4cc365f8b
--- /dev/null
+++ b/tests/test_ingredient.py
@@ -0,0 +1,56 @@
+"""Тесты для класса Ingredient"""
+import pytest
+from praktikum.ingredient import Ingredient
+from tests.test_data import TEST_INGREDIENTS
+
+
+class TestIngredient:
+ """Тестовый класс для проверки класса Ingredient"""
+
+ @pytest.mark.parametrize("test_data", [
+ TEST_INGREDIENTS[0], # SAUCE, hot sauce, 100
+ TEST_INGREDIENTS[1], # SAUCE, sour cream, 200
+ TEST_INGREDIENTS[4], # FILLING, cutlet, 100
+ TEST_INGREDIENTS[5], # FILLING, dinosaur, 200
+ ])
+ def test_ingredient_initialization(self, test_data):
+ """Тест инициализации ингредиента с параметризацией"""
+
+ ingredient = Ingredient(test_data["type"], test_data["name"], test_data["price"])
+
+
+ assert ingredient.type == test_data["type"]
+ assert ingredient.name == test_data["name"]
+ assert ingredient.price == test_data["price"]
+
+ @pytest.mark.parametrize("price", [100.0, 0.0, 50.5, 999.99])
+ def test_get_price_returns_correct_value(self, price):
+ """Тест метода get_price с параметризацией"""
+
+ ingredient = Ingredient("SAUCE", "test", price)
+
+
+ assert ingredient.get_price() == price
+
+ @pytest.mark.parametrize("test_data", [
+ TEST_INGREDIENTS[0], # hot sauce
+ TEST_INGREDIENTS[1], # sour cream
+ TEST_INGREDIENTS[4], # cutlet
+ TEST_INGREDIENTS[5], # dinosaur
+ ])
+ def test_get_name_returns_correct_value(self, test_data):
+ """Тест метода get_name с параметризацией"""
+
+ ingredient = Ingredient(test_data["type"], test_data["name"], 100)
+
+
+ assert ingredient.get_name() == test_data["name"]
+
+ @pytest.mark.parametrize("ingredient_type", ["SAUCE", "FILLING"])
+ def test_get_type_returns_correct_value(self, ingredient_type):
+ """Тест метода get_type с параметризацией"""
+
+ ingredient = Ingredient(ingredient_type, "test", 100)
+
+
+ assert ingredient.get_type() == ingredient_type
diff --git a/tests/test_praktikum.py b/tests/test_praktikum.py
new file mode 100644
index 000000000..17ac4adf7
--- /dev/null
+++ b/tests/test_praktikum.py
@@ -0,0 +1,18 @@
+"""Тесты для основного модуля praktikum"""
+import pytest
+import sys
+from io import StringIO
+
+def test_main_executes_without_error():
+ """Тест что main выполняется без ошибок"""
+ from praktikum.praktikum import main
+ main()
+
+def test_main_has_output(capsys):
+ """Тест что main выводит результат"""
+ from praktikum.praktikum import main
+
+ main()
+ captured = capsys.readouterr()
+
+ assert captured.out != ""
\ No newline at end of file