diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..cd5cae788 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ + +venv/ +.env/ +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.pytest_cache/ +.coverage +htmlcov/ +.idea/ +.vscode/ +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/README.md b/README.md index 272081708..eb993c309 100644 --- a/README.md +++ b/README.md @@ -2,23 +2,71 @@ ### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers +В проекте реализованы юнит-тесты для программы, которая помогает заказать бургер в Stellar Burgers. + +Тестами покрыт класс Burger из пакета praktikum. + +🏁 Покрытие кода класса Burger — 100% +(HTML-отчёт: htmlcov/index.html) + ### Реализованные сценарии +В проекте реализованы юнит-тесты для программы, которая помогает заказать бургер в Stellar Burgers. -Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database` +Тестами покрыт класс Burger из пакета praktikum. -Процент покрытия 100% (отчет: `htmlcov/index.html`) +🏁 Покрытие кода класса Burger — 100% +HTML-отчёт: htmlcov/index.html + +Проверены методы класса Burger: + +set_buns +add_ingredient +remove_ingredient +move_ingredient +get_price +get_receipt + +В тестах используются: +моки (unittest.mock) — для изоляции зависимостей (Bun, Ingredient) +параметризация (pytest.mark.parametrize) -### Структура проекта -- `praktikum` - пакет, содержащий код программы -- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. +Используемые библиотеки : -### Запуск автотестов +pytest +pytest-cov +### Структура проекта +Diplom_1 +├── praktikum +│ ├── __init__.py +│ ├── bun.py +│ ├── burger.py +│ ├── database.py +│ ├── ingredient.py +│ └── ingredient_types.py +├── tests +│ └── test_burger.py +├── pytest.ini +├── requirements.txt +└── README.md +└── .gitignore **Установка зависимостей** -> `$ pip install -r requirements.txt` +> pip install -r requirements.txt **Запуск автотестов и создание HTML-отчета о покрытии** -> `$ pytest --cov=praktikum --cov-report=html` +> python -m pytest tests -q + +Покрытие проверяется для класса Burger: + +> python -m pytest tests --cov=praktikum.burger --cov-report=term-missing + +HTML-отчёт можно получить командой: + +> python -m pytest tests --cov=praktikum.burger --cov-report=html + +Отчёт будет доступен в папке: + +> htmlcov/index.html \ No newline at end of file diff --git a/praktikum.py b/praktikum.py deleted file mode 100644 index ec522fa6d..000000000 --- a/praktikum.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import List - -from praktikum.bun import Bun -from praktikum.burger import Burger -from praktikum.database import Database -from praktikum.ingredient import Ingredient - - -def main(): - # Инициализируем базу данных - database: Database = Database() - - # Создадим новый бургер - burger: Burger = Burger() - - # Считаем список доступных булок из базы данных - buns: List[Bun] = database.available_buns() - - # Считаем список доступных ингредиентов из базы данных - ingredients: List[Ingredient] = database.available_ingredients() - - # Соберём бургер - burger.set_buns(buns[0]) - - burger.add_ingredient(ingredients[1]) - burger.add_ingredient(ingredients[4]) - burger.add_ingredient(ingredients[3]) - burger.add_ingredient(ingredients[5]) - - # Переместим слой с ингредиентом - burger.move_ingredient(2, 1) - - # Удалим ингредиент - burger.remove_ingredient(3) - - # Распечатаем рецепт бургера - print(burger.get_receipt()) - - -if __name__ == "__main__": - main() 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 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/pytest.ini b/pytest.ini new file mode 100644 index 000000000..306e00bd6 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +pythonpath = . +testpaths = tests \ No newline at end of file diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..486d81297 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,98 @@ +import pytest +from unittest.mock import Mock + +from praktikum.burger import Burger + + +@pytest.fixture +def burger(): + return Burger() + + +@pytest.fixture +def bun(): + bun = Mock() + bun.get_name.return_value = "black bun" + bun.get_price.return_value = 100 + return bun + + +@pytest.fixture +def ingredient_sauce(): + ing = Mock() + ing.get_type.return_value = "SAUCE" + ing.get_name.return_value = "hot sauce" + ing.get_price.return_value = 50 + return ing + + +@pytest.fixture +def ingredient_filling(): + ing = Mock() + ing.get_type.return_value = "FILLING" + ing.get_name.return_value = "cutlet" + ing.get_price.return_value = 200 + return ing + + +def test_set_buns_sets_bun(burger, bun): + burger.set_buns(bun) + assert burger.bun == bun + + +@pytest.mark.parametrize("count", [1, 2, 3]) +def test_add_ingredient_appends_to_list(burger, ingredient_sauce, count): + for _ in range(count): + burger.add_ingredient(ingredient_sauce) + assert len(burger.ingredients) == count + assert burger.ingredients[-1] == ingredient_sauce + + +def test_remove_ingredient_deletes_by_index(burger, ingredient_sauce, ingredient_filling): + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + + burger.remove_ingredient(0) + + assert burger.ingredients == [ingredient_filling] + + +def test_move_ingredient_changes_order(burger, ingredient_sauce, ingredient_filling): + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + + burger.move_ingredient(0, 1) + + assert burger.ingredients == [ingredient_filling, ingredient_sauce] + + +def test_get_price_counts_bun_twice_and_ingredients(burger, bun, ingredient_sauce, ingredient_filling): + burger.set_buns(bun) + burger.add_ingredient(ingredient_sauce) + burger.add_ingredient(ingredient_filling) + + assert burger.get_price() == 100 * 2 + 50 + 200 + bun.get_price.assert_called() + ingredient_sauce.get_price.assert_called() + ingredient_filling.get_price.assert_called() + + +def test_get_price_with_no_ingredients(burger, bun): + burger.set_buns(bun) + assert burger.get_price() == 100 * 2 + + +def test_get_receipt_format_and_content(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 f"(==== {bun.get_name()} ====)" in receipt + assert f"(==== {bun.get_name()} ====)\n" in receipt + + assert f"= sauce {ingredient_sauce.get_name()} =" in receipt + assert f"= filling {ingredient_filling.get_name()} =" in receipt + + assert f"Price: {burger.get_price()}" in receipt \ No newline at end of file