diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8a366f7f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +__pycache__/ +.pytest_cache/ +htmlcov/ +.coverage +.venv/ +venv/ +.idea/ +.vscode/ +*.pyc \ No newline at end of file diff --git a/README.md b/README.md index 272081708..ddef3aaa7 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,46 @@ -## Задание 1: Юнит-тесты +# Задание 1: Юнит-тесты -### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers +## Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers -### Реализованные сценарии +Проект содержит юнит-тесты для классов `Bun`, `Burger`, `Ingredient`, `Database`. -Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database` +## Что используется в тестах -Процент покрытия 100% (отчет: `htmlcov/index.html`) +- `pytest` +- `pytest-cov` +- параметризация +- моки -### Структура проекта +## Структура проекта -- `praktikum` - пакет, содержащий код программы -- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. +- `praktikum` — пакет с кодом приложения +- `tests` — пакет с юнит-тестами +- `main.py` — файл запуска программы +- `requirements.txt` — зависимости проекта +- `.gitignore` — список игнорируемых файлов и папок -### Запуск автотестов +## Установка зависимостей -**Установка зависимостей** +```bash +pip install -r requirements.txt +``` -> `$ pip install -r requirements.txt` +## Запуск тестов -**Запуск автотестов и создание HTML-отчета о покрытии** +```bash +python -m pytest -v +``` -> `$ pytest --cov=praktikum --cov-report=html` +## Запуск тестов с отчётом покрытия + +```bash +python -m pytest --cov=praktikum --cov-report=term-missing --cov-report=html +``` + +## Отчёт покрытия + +После запуска команды с `pytest-cov` HTML-отчёт будет доступен в файле: + +```bash +htmlcov/index.html +``` \ No newline at end of file diff --git a/praktikum.py b/main.py similarity index 100% rename from praktikum.py rename to main.py 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 97% rename from database.py rename to praktikum/database.py index 4c75baf71..a202dcc79 100644 --- a/database.py +++ b/praktikum/database.py @@ -30,4 +30,4 @@ def available_buns(self) -> List[Bun]: return self.buns def available_ingredients(self) -> List[Ingredient]: - return self.ingredients + return self.ingredients \ No newline at end of file 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 80% rename from ingredient_types.py rename to praktikum/ingredient_types.py index 34940ad5d..d454c885d 100644 --- a/ingredient_types.py +++ b/praktikum/ingredient_types.py @@ -3,5 +3,6 @@ SAUCE – соус FILLING – начинка """ + INGREDIENT_TYPE_SAUCE = 'SAUCE' -INGREDIENT_TYPE_FILLING = 'FILLING' +INGREDIENT_TYPE_FILLING = 'FILLING' \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..5768c1869 Binary files /dev/null and b/requirements.txt differ diff --git a/tests/test_bun.py b/tests/test_bun.py new file mode 100644 index 000000000..27edea436 --- /dev/null +++ b/tests/test_bun.py @@ -0,0 +1,24 @@ +import pytest + +from praktikum.bun import Bun + + +class TestBun: + + @pytest.mark.parametrize("name, price", [ + ("black bun", 100), + ("white bun", 200.5) + ]) + def test_get_name_returns_correct_name(self, name, price): + bun = Bun(name, price) + + assert bun.get_name() == name + + @pytest.mark.parametrize("name, price", [ + ("black bun", 100), + ("white bun", 200.5) + ]) + def test_get_price_returns_correct_price(self, name, price): + bun = Bun(name, price) + + assert bun.get_price() == 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..f6ebacd7e --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,87 @@ +from unittest.mock import MagicMock + +from praktikum.bun import Bun +from praktikum.burger import Burger +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestBurger: + + def test_set_buns_sets_bun(self): + burger = Burger() + bun = Bun("black bun", 100) + + burger.set_buns(bun) + + assert burger.bun == bun + + def test_add_ingredient_adds_ingredient_to_list(self): + burger = Burger() + ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + + burger.add_ingredient(ingredient) + + assert burger.ingredients == [ingredient] + + def test_remove_ingredient_removes_ingredient_by_index(self): + burger = Burger() + first_ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + second_ingredient = Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 200) + + burger.add_ingredient(first_ingredient) + burger.add_ingredient(second_ingredient) + burger.remove_ingredient(0) + + assert burger.ingredients == [second_ingredient] + + def test_move_ingredient_moves_ingredient_to_new_index(self): + burger = Burger() + first_ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + second_ingredient = Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 200) + third_ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300) + + burger.add_ingredient(first_ingredient) + burger.add_ingredient(second_ingredient) + burger.add_ingredient(third_ingredient) + burger.move_ingredient(0, 2) + + assert burger.ingredients == [second_ingredient, third_ingredient, first_ingredient] + + def test_get_price_returns_correct_price(self): + burger = Burger() + + bun = MagicMock() + bun.get_price.return_value = 100 + + first_ingredient = MagicMock() + first_ingredient.get_price.return_value = 50 + + second_ingredient = MagicMock() + second_ingredient.get_price.return_value = 70 + + burger.set_buns(bun) + burger.add_ingredient(first_ingredient) + burger.add_ingredient(second_ingredient) + + assert burger.get_price() == 320 + + def test_get_receipt_returns_correct_receipt(self): + burger = Burger() + bun = Bun("black bun", 100) + first_ingredient = Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100) + second_ingredient = Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100) + + burger.set_buns(bun) + burger.add_ingredient(first_ingredient) + burger.add_ingredient(second_ingredient) + + expected_receipt = ( + "(==== black bun ====)\n" + "= sauce hot sauce =\n" + "= filling cutlet =\n" + "(==== black bun ====)\n\n" + "Price: 400" + ) + + assert burger.get_receipt() == expected_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..fd8d523a6 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,97 @@ +import pytest + +from praktikum.database import Database +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +EXPECTED_BUNS = [ + (0, "black bun", 100), + (1, "white bun", 200), + (2, "red bun", 300), +] + +EXPECTED_INGREDIENTS = [ + (0, INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (1, INGREDIENT_TYPE_SAUCE, "sour cream", 200), + (2, INGREDIENT_TYPE_SAUCE, "chili sauce", 300), + (3, INGREDIENT_TYPE_FILLING, "cutlet", 100), + (4, INGREDIENT_TYPE_FILLING, "dinosaur", 200), + (5, INGREDIENT_TYPE_FILLING, "sausage", 300), +] + + +class TestDatabase: + + def test_available_buns_returns_three_buns(self): + database = Database() + buns = database.available_buns() + + assert len(buns) == 3 + + @pytest.mark.parametrize("index, expected_name, expected_price", EXPECTED_BUNS) + def test_available_buns_returns_buns_with_correct_name(self, index, expected_name, expected_price): + database = Database() + bun = database.available_buns()[index] + + assert bun.get_name() == expected_name + + @pytest.mark.parametrize("index, expected_name, expected_price", EXPECTED_BUNS) + def test_available_buns_returns_buns_with_correct_price(self, index, expected_name, expected_price): + database = Database() + bun = database.available_buns()[index] + + assert bun.get_price() == expected_price + + def test_available_ingredients_returns_six_ingredients(self): + database = Database() + ingredients = database.available_ingredients() + + assert len(ingredients) == 6 + + @pytest.mark.parametrize( + "index, expected_type, expected_name, expected_price", + EXPECTED_INGREDIENTS + ) + def test_available_ingredients_returns_ingredients_with_correct_type( + self, + index, + expected_type, + expected_name, + expected_price + ): + database = Database() + ingredient = database.available_ingredients()[index] + + assert ingredient.get_type() == expected_type + + @pytest.mark.parametrize( + "index, expected_type, expected_name, expected_price", + EXPECTED_INGREDIENTS + ) + def test_available_ingredients_returns_ingredients_with_correct_name( + self, + index, + expected_type, + expected_name, + expected_price + ): + database = Database() + ingredient = database.available_ingredients()[index] + + assert ingredient.get_name() == expected_name + + @pytest.mark.parametrize( + "index, expected_type, expected_name, expected_price", + EXPECTED_INGREDIENTS + ) + def test_available_ingredients_returns_ingredients_with_correct_price( + self, + index, + expected_type, + expected_name, + expected_price + ): + database = Database() + ingredient = database.available_ingredients()[index] + + assert ingredient.get_price() == expected_price \ No newline at end of file diff --git a/tests/test_ingredient.py b/tests/test_ingredient.py new file mode 100644 index 000000000..e9ef1598e --- /dev/null +++ b/tests/test_ingredient.py @@ -0,0 +1,34 @@ +import pytest + +from praktikum.ingredient import Ingredient +from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + + +class TestIngredient: + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 300) + ]) + def test_get_type_returns_correct_type(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_type() == ingredient_type + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 300) + ]) + def test_get_name_returns_correct_name(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_name() == name + + @pytest.mark.parametrize("ingredient_type, name, price", [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 100), + (INGREDIENT_TYPE_FILLING, "cutlet", 300) + ]) + def test_get_price_returns_correct_price(self, ingredient_type, name, price): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_price() == price \ No newline at end of file