diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..20623a09c --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Виртуальное окружение +.venv/ +venv/ +env/ + +# Кэш Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +.ipynb_checkpoints + +# Coverage +.coverage +htmlcov/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# ОС +.DS_Store +Thumbs.db + +# pytest +.pytest_cache/ \ No newline at end of file diff --git a/README.md b/README.md index 272081708..130fb8b3c 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,56 @@ -## Задание 1: Юнит-тесты - -### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers - -### Реализованные сценарии - -Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database` - -Процент покрытия 100% (отчет: `htmlcov/index.html`) - -### Структура проекта - -- `praktikum` - пакет, содержащий код программы -- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. - -### Запуск автотестов - -**Установка зависимостей** - +Задание 1: Юнит-тесты +Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers + +Реализованные сценарии + +Созданы юнит-тесты, покрывающие классы `Burger` +Класс `Burger` покрыт на 100% — согласно отчёту `htmlcov/index.html`. +Name Stmts Miss Cover +--------------------------------------------------- +praktikum\bun.py 8 1 88% +praktikum\burger.py 27 0 100% +praktikum\database.py 21 21 0% +praktikum\ingredient.py 11 2 82% +praktikum\ingredient_types.py 2 0 100% +praktikum\praktikum.py 20 20 0% +--------------------------------------------------- +TOTAL 89 44 51% + + +Проверены: + +Установка булочки (set_buns) +Добавление ингредиентов (add_ingredient) +Удаление ингредиентов по индексу (remove_ingredient) +Перемещение ингредиентов (move_ingredient) +Расчёт итоговой стоимости (get_price) +Формирование чека (get_receipt) — здесь использован unittest.mock.Mock + +DIPLOM_1/ +├── htmlcov/ # Отчет о покрытии +├── praktikum/ # Основной код приложения +│ ├── __init__.py +│ ├── bun.py +│ ├── burger.py +│ ├── database.py +│ ├── ingredient.py +│ ├── ingredient_types.py +│ └── praktikum.py +├── tests/ # Тесты +│ ├── __init__.py +│ └── test_burger.py +├── .gitignore +├── README.md # описание проекта +└── requirements.txt # Зависимости + + + +Установка зависимостей > `$ pip install -r requirements.txt` -**Запуск автотестов и создание HTML-отчета о покрытии** - +Запуск автотестов и создание HTML-отчета о покрытии > `$ pytest --cov=praktikum --cov-report=html` + +Примечание +Bun и Ingredient — простые объекты данных без побочных эффектов, поэтому в большинстве тестов они используются «как есть». +Мок применяется только в тесте get_receipt(), чтобы изолировать форматирование чека от реализации других классов и гарантировать, что тест проверяет именно логику вывода, а не работу зависимостей. 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 100% rename from burger.py rename to praktikum/burger.py diff --git a/database.py b/praktikum/database.py similarity index 100% rename from database.py rename to praktikum/database.py 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..bfc2333d3 Binary files /dev/null and b/requirements.txt differ diff --git a/__init__.py b/tests/__init__.py similarity index 100% rename from __init__.py rename to tests/__init__.py diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..5f3714871 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,99 @@ +import pytest +from unittest.mock import Mock + +from praktikum.burger import Burger +from praktikum.bun import Bun +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestBurger: + + @pytest.mark.parametrize("name, price", [("black bun", 100),("white bun", 200),]) + def test_set_buns_sets_bun_successfully(self, name, price): + burger = Burger() + bun = Bun(name, price) + burger.set_buns(bun) + assert burger.bun is bun + + @pytest.mark.parametrize("type, name, price", [(INGREDIENT_TYPE_SAUCE, "hot sauce", 100),(INGREDIENT_TYPE_FILLING, "cutlet", 100),(INGREDIENT_TYPE_SAUCE, "chili sauce", 300),]) + def test_add_ingredient_successfully(self, type, name, price): + burger = Burger() + ingredient = Ingredient(type, name, price) + burger.add_ingredient(ingredient) + assert ingredient in burger.ingredients + + def test_remove_ingredient_removes_ingredient_by_index_successfully(self): + burger = Burger() + cutlet = Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100) + dinosaur = Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200) + sauce = Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300) + burger.add_ingredient(cutlet) + burger.add_ingredient(dinosaur) + burger.add_ingredient(sauce) + burger.remove_ingredient(1) + assert len(burger.ingredients) == 2 + assert cutlet in burger.ingredients + assert sauce in burger.ingredients + assert dinosaur not in burger.ingredients + + def test_move_ingredient_moves_ingredient_by_index_successfully(self): + burger = Burger() + sauce = Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300) + cutlet = Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100) + dinosaur = Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200) + hot_sauce = Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + burger.add_ingredient(sauce) + burger.add_ingredient(cutlet) + burger.add_ingredient(dinosaur) + burger.add_ingredient(hot_sauce) + burger.move_ingredient(2,3) + assert len(burger.ingredients) == 4 + assert burger.ingredients[0] is sauce + assert burger.ingredients[1] is cutlet + assert burger.ingredients[2] is hot_sauce + assert burger.ingredients[3] is dinosaur + + + @pytest.mark.parametrize('bun_name, bun_price, ingredients_data, expected_price', + [("black bun", 100, [(INGREDIENT_TYPE_FILLING, "cutlet", 100)], 300), + ("black bun", 100, [], 200), + ("white bun", 200, [(INGREDIENT_TYPE_FILLING, "dinosaur", 200), (INGREDIENT_TYPE_SAUCE, "hot sauce", 100)], 700)]) + def test_get_price_cost_of_a_ready_made_burger(self, bun_name, bun_price, ingredients_data, expected_price ): + burger = Burger() + bun = Bun(bun_name, bun_price) + burger.set_buns(bun) + + for type, name, price in ingredients_data: + ingredient = Ingredient(type, name, price) + burger.add_ingredient(ingredient) + + price = burger.get_price() + assert price == expected_price + + + def test_get_receipt_uses_mocked_bun_and_ingredient(self): + bun="red bun" + bun_price=300 + + ingredient_type=INGREDIENT_TYPE_FILLING + ingredient_name="cutlet" + ingredient_price=200 + + mock_bun = Mock() + mock_bun.get_name.return_value = bun + mock_bun.get_price.return_value = bun_price + + mock_ingredient = Mock() + mock_ingredient.get_type.return_value = ingredient_type + mock_ingredient.get_name.return_value = ingredient_name + mock_ingredient.get_price.return_value = ingredient_price + + burger = Burger() + burger.set_buns(mock_bun) + burger.add_ingredient(mock_ingredient) + + receipt = burger.get_receipt() + + expected = f"(==== {bun} ====)\n= {ingredient_type.lower()} {ingredient_name} =\n(==== {bun} ====)\n\nPrice: {bun_price*2 + ingredient_price}" + assert receipt == expected \ No newline at end of file