From f4ef55055d1b9d843acfa25edc7dc880776d0cc9 Mon Sep 17 00:00:00 2001 From: Darya Melnik Date: Tue, 18 Nov 2025 21:23:52 +0100 Subject: [PATCH] Add unit tests, ensure required coverage, and clean up project according to task requirements --- .gitignore | 119 +++++++++++++++++++++++++++++++++++++++ __init__.py | 0 bun.py | 15 ----- burger.py | 48 ---------------- data.py | 19 +++++++ database.py | 33 ----------- ingredient.py | 20 ------- ingredient_types.py | 7 --- praktikum.py => main.py | 0 requirements.txt | 2 + tests/conftest.py | 22 ++++++++ tests/test_bun.py | 10 ++++ tests/test_burger.py | 49 ++++++++++++++++ tests/test_database.py | 17 ++++++ tests/test_ingredient.py | 15 +++++ 15 files changed, 253 insertions(+), 123 deletions(-) create mode 100644 .gitignore delete mode 100644 __init__.py delete mode 100644 bun.py delete mode 100644 burger.py create mode 100644 data.py delete mode 100644 database.py delete mode 100644 ingredient.py delete mode 100644 ingredient_types.py rename praktikum.py => main.py (100%) create mode 100644 requirements.txt create mode 100644 tests/conftest.py create mode 100644 tests/test_bun.py create mode 100644 tests/test_burger.py create mode 100644 tests/test_database.py create mode 100644 tests/test_ingredient.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..5a507ec92 --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyderworkspace + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Ignore the symbolic link +praktikum diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/bun.py b/bun.py deleted file mode 100644 index 5504bc1f4..000000000 --- a/bun.py +++ /dev/null @@ -1,15 +0,0 @@ -class Bun: - """ - Модель булочки для бургера. - Булочке можно дать название и назначить цену. - """ - - def __init__(self, name: str, price: float): - self.name = name - self.price = price - - def get_name(self) -> str: - return self.name - - def get_price(self) -> float: - return self.price diff --git a/burger.py b/burger.py deleted file mode 100644 index 2b3b6a88b..000000000 --- a/burger.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import List - -from praktikum.bun import Bun -from praktikum.ingredient import Ingredient - - -class Burger: - """ - Модель бургера. - Бургер состоит из булочек и ингредиентов (начинка или соус). - Ингредиенты можно перемещать и удалять. - Можно распечать чек с информацией о бургере. - """ - - def __init__(self): - self.bun = None - self.ingredients: List[Ingredient] = [] - - def set_buns(self, bun: Bun): - self.bun = bun - - def add_ingredient(self, ingredient: Ingredient): - self.ingredients.append(ingredient) - - def remove_ingredient(self, index: int): - del self.ingredients[index] - - def move_ingredient(self, index: int, new_index: int): - self.ingredients.insert(new_index, self.ingredients.pop(index)) - - def get_price(self) -> float: - price = self.bun.get_price() * 2 - - for ingredient in self.ingredients: - price += ingredient.get_price() - - return price - - def get_receipt(self) -> str: - receipt: List[str] = [f'(==== {self.bun.get_name()} ====)'] - - for ingredient in self.ingredients: - receipt.append(f'= {str(ingredient.get_type()).lower()} {ingredient.get_name()} =') - - receipt.append(f'(==== {self.bun.get_name()} ====)\n') - receipt.append(f'Price: {self.get_price()}') - - return '\n'.join(receipt) diff --git a/data.py b/data.py new file mode 100644 index 000000000..e72a0973c --- /dev/null +++ b/data.py @@ -0,0 +1,19 @@ +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + +class Data: + BUNS = [ + Bun("black bun", 100), + Bun("white bun", 200), + Bun("red bun", 300) + ] + + INGREDIENTS = [ + Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + Ingredient(INGREDIENT_TYPE_SAUCE, "sour cream", 200), + Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300), + Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100), + Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200), + Ingredient(INGREDIENT_TYPE_FILLING, "sausage", 300) + ] diff --git a/database.py b/database.py deleted file mode 100644 index 4c75baf71..000000000 --- a/database.py +++ /dev/null @@ -1,33 +0,0 @@ -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: - """ - Класс с методами по работе с базой данных. - """ - - def __init__(self): - self.buns: List[Bun] = [] - self.ingredients: List[Ingredient] = [] - - self.buns.append(Bun("black bun", 100)) - self.buns.append(Bun("white bun", 200)) - self.buns.append(Bun("red bun", 300)) - - self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100)) - self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "sour cream", 200)) - self.ingredients.append(Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300)) - - self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100)) - self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200)) - self.ingredients.append(Ingredient(INGREDIENT_TYPE_FILLING, "sausage", 300)) - - def available_buns(self) -> List[Bun]: - return self.buns - - def available_ingredients(self) -> List[Ingredient]: - return self.ingredients diff --git a/ingredient.py b/ingredient.py deleted file mode 100644 index 0e50db8a2..000000000 --- a/ingredient.py +++ /dev/null @@ -1,20 +0,0 @@ -class Ingredient: - """ - Модель ингредиента. - Ингредиент: начинка или соус. - У ингредиента есть тип (начинка или соус), название и цена. - """ - - def __init__(self, ingredient_type: str, name: str, price: float): - self.type = ingredient_type - self.name = name - self.price = price - - def get_price(self) -> float: - return self.price - - def get_name(self) -> str: - return self.name - - def get_type(self) -> str: - return self.type diff --git a/ingredient_types.py b/ingredient_types.py deleted file mode 100644 index 34940ad5d..000000000 --- a/ingredient_types.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Перечисление с типами ингредиентов. -SAUCE – соус -FILLING – начинка -""" -INGREDIENT_TYPE_SAUCE = 'SAUCE' -INGREDIENT_TYPE_FILLING = 'FILLING' diff --git a/praktikum.py b/main.py similarity index 100% rename from praktikum.py rename to main.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..cffeec658 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest +pytest-cov \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..5b1299c7e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,22 @@ +import pytest +from unittest.mock import patch +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + +@pytest.fixture() +def mock_random_choice(): + with patch('random.choice') as mocked_choice: + yield mocked_choice + +@pytest.fixture +def bun(): + return Bun("black bun", 100) + +@pytest.fixture +def ingredient_sauce(): + return Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + +@pytest.fixture +def ingredient_filling(): + return Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 200) \ No newline at end of file diff --git a/tests/test_bun.py b/tests/test_bun.py new file mode 100644 index 000000000..8010b7eb6 --- /dev/null +++ b/tests/test_bun.py @@ -0,0 +1,10 @@ +from praktikum.bun import Bun + +class TestBun: + def test_get_name(self): + bun = Bun("black bun", 100) + assert bun.get_name() == "black bun" + + def test_get_price(self): + bun = Bun("black bun", 100) + assert bun.get_price() == 100 diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..95c967263 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,49 @@ +import pytest +from praktikum.burger import Burger + +@pytest.fixture +def burger(): + return Burger() + +class TestBurger: + def test_set_buns(self, burger, bun): + burger.set_buns(bun) + assert burger.bun == bun + + def test_add_ingredient(self, burger, ingredient_sauce): + burger.add_ingredient(ingredient_sauce) + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == ingredient_sauce + + def test_remove_ingredient(self, burger, ingredient_sauce, ingredient_filling): + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + burger.remove_ingredient(0) + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == ingredient_filling + + def test_move_ingredient(self, burger, ingredient_sauce, ingredient_filling): + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + burger.move_ingredient(0, 1) + assert burger.ingredients[0] == ingredient_filling + assert burger.ingredients[1] == ingredient_sauce + + def test_get_price(self, burger, bun, ingredient_sauce, ingredient_filling): + burger.set_buns(bun) + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + expected_price = bun.get_price() * 2 + ingredient_sauce.get_price() + ingredient_filling.get_price() + assert burger.get_price() == expected_price + + def test_get_receipt(self, burger, bun, ingredient_sauce, ingredient_filling): + burger.set_buns(bun) + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + + receipt = burger.get_receipt() + + assert bun.get_name() in receipt + assert ingredient_sauce.get_name() in receipt + assert ingredient_filling.get_name() in receipt + assert str(burger.get_price()) in receipt \ No newline at end of file diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 000000000..947e4627b --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,17 @@ +from unittest.mock import patch +from praktikum.database import Database + +class TestDatabase: + def test_available_buns(self, mock_random_choice): + db = Database() + mock_bun = db.buns[0] + mock_random_choice.return_value = mock_bun + bun = db.available_buns() + assert bun == mock_bun + + def test_available_ingredients(self, mock_random_choice): + db = Database() + mock_ingredient = db.ingredients[0] + mock_random_choice.return_value = mock_ingredient + ingredient = db.available_ingredients() + assert ingredient == mock_ingredient \ No newline at end of file diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py new file mode 100644 index 000000000..09c5ff84e --- /dev/null +++ b/tests/test_ingredient.py @@ -0,0 +1,15 @@ +import pytest +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + +class TestIngredient: + @pytest.mark.parametrize("expected_type, expected_name, expected_price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 200), + (INGREDIENT_TYPE_SAUCE, "chili sauce", 300.5) + ]) + def test_getters(self, expected_type, expected_name, expected_price): + ingredient = Ingredient(expected_type, expected_name, expected_price) + assert ingredient.get_type() == expected_type + assert ingredient.get_name() == expected_name + assert ingredient.get_price() == expected_price