diff --git a/README.md b/README.md index 272081708..6f2477ead 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Задание 1: Юнит-тесты +## Дипломный проект. Задание 1: Юнит-тесты ### Автотесты для проверки программы, которая помогает заказать бургер в Stellar Burgers diff --git a/burger.py b/burger.py index 2b3b6a88b..3f71aaa29 100644 --- a/burger.py +++ b/burger.py @@ -1,7 +1,7 @@ from typing import List -from praktikum.bun import Bun -from praktikum.ingredient import Ingredient +from bun import Bun +from ingredient import Ingredient class Burger: diff --git a/database.py b/database.py index 4c75baf71..84d3685d0 100644 --- a/database.py +++ b/database.py @@ -1,8 +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 +from bun import Bun +from ingredient import Ingredient +from ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING class Database: diff --git a/praktikum.py b/praktikum.py index ec522fa6d..88b77abd1 100644 --- a/praktikum.py +++ b/praktikum.py @@ -1,9 +1,9 @@ from typing import List -from praktikum.bun import Bun -from praktikum.burger import Burger -from praktikum.database import Database -from praktikum.ingredient import Ingredient +from bun import Bun +from burger import Burger +from database import Database +from ingredient import Ingredient def main(): diff --git a/test_burger.py b/test_burger.py new file mode 100644 index 000000000..a82b67f99 --- /dev/null +++ b/test_burger.py @@ -0,0 +1,230 @@ +import pytest +from unittest.mock import Mock +from burger import Burger +from bun import Bun +from ingredient import Ingredient +from ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING +from test_data import GET_RECEIPT_TEST_DATA, GET_PRICE_TEST_DATA + + +class TestBurger: + + # Тесты для метода set_buns + def test_set_buns(self): + burger = Burger() + mock_bun = Mock(spec=Bun) + + burger.set_buns(mock_bun) + + assert burger.bun == mock_bun + + # Тесты для метода add_ingredient + def test_add_ingredient(self): + burger = Burger() + mock_ingredient = Mock(spec=Ingredient) + + burger.add_ingredient(mock_ingredient) + + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == mock_ingredient + + # Тесты для метода remove_ingredient + def test_remove_ingredient(self): + burger = Burger() + mock_ingredient1 = Mock(spec=Ingredient) + mock_ingredient2 = Mock(spec=Ingredient) + + burger.add_ingredient(mock_ingredient1) + burger.add_ingredient(mock_ingredient2) + + burger.remove_ingredient(0) + + assert len(burger.ingredients) == 1 + assert burger.ingredients[0] == mock_ingredient2 + + # Тесты для метода move_ingredient + def test_move_ingredient(self): + burger = Burger() + mock_ingredient1 = Mock(spec=Ingredient) + mock_ingredient2 = Mock(spec=Ingredient) + mock_ingredient3 = Mock(spec=Ingredient) + + burger.add_ingredient(mock_ingredient1) + burger.add_ingredient(mock_ingredient2) + burger.add_ingredient(mock_ingredient3) + + burger.move_ingredient(0, 2) + + assert burger.ingredients[0] == mock_ingredient2 + assert burger.ingredients[1] == mock_ingredient3 + assert burger.ingredients[2] == mock_ingredient1 + + @pytest.mark.parametrize("test_data", GET_PRICE_TEST_DATA) + def test_get_price_with_different_combinations(self, test_data): + burger = Burger() + + # Мок булки + mock_bun = Mock(spec=Bun) + mock_bun.get_price.return_value = test_data["bun_price"] + burger.set_buns(mock_bun) + + # Моки ингредиентов + for price in test_data["ingredient_prices"]: + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_price.return_value = price + burger.add_ingredient(mock_ingredient) + + result = burger.get_price() + + # Убираем лишние строки после assert + assert result == test_data["expected_total"] + + # Тест для метода get_price с проверкой вызовов методов + def test_get_price_calls_methods(self): + burger = Burger() + + mock_bun = Mock(spec=Bun) + mock_bun.get_price.return_value = 100 + burger.set_buns(mock_bun) + + mock_ingredient1 = Mock(spec=Ingredient) + mock_ingredient1.get_price.return_value = 50 + burger.add_ingredient(mock_ingredient1) + + mock_ingredient2 = Mock(spec=Ingredient) + mock_ingredient2.get_price.return_value = 75 + burger.add_ingredient(mock_ingredient2) + + result = burger.get_price() + + assert result == 325 + + # Параметризованные тесты для метода get_receipt + @pytest.mark.parametrize("test_data", GET_RECEIPT_TEST_DATA) + def test_get_receipt_format(self, test_data): + burger = Burger() + + # Мок булки + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = test_data["bun_name"] + mock_bun.get_price.return_value = 0 + burger.set_buns(mock_bun) + + # Моки ингредиентов + for ingredient_type, ingredient_name in test_data["ingredients"]: + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_type.return_value = ingredient_type + mock_ingredient.get_name.return_value = ingredient_name + mock_ingredient.get_price.return_value = 0 + burger.add_ingredient(mock_ingredient) + + receipt = burger.get_receipt() + receipt_lines = receipt.split('\n') + + # Убираем лишние строки после assert + assert len(receipt_lines) == len(test_data["expected_lines"]) + for i, expected_line in enumerate(test_data["expected_lines"]): + assert receipt_lines[i] == expected_line + + # Тест для метода get_receipt с правильной ценой + def test_get_receipt_with_price(self): + burger = Burger() + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "red bun" + mock_bun.get_price.return_value = 100 + burger.set_buns(mock_bun) + + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_type.return_value = INGREDIENT_TYPE_SAUCE + mock_ingredient.get_name.return_value = "chili sauce" + mock_ingredient.get_price.return_value = 50 + burger.add_ingredient(mock_ingredient) + + receipt = burger.get_receipt() + + assert "Price: 250" in receipt # 100*2 + 50 = 250 + assert "(==== red bun ====)" in receipt + assert "= sauce chili sauce =" in receipt + + # Тест для пустого бургера (только булки) + def test_get_receipt_only_bun(self): + burger = Burger() + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "white bun" + mock_bun.get_price.return_value = 200 + burger.set_buns(mock_bun) + + receipt = burger.get_receipt() + receipt_lines = receipt.split('\n') + + expected_lines = [ + "(==== white bun ====)", + "(==== white bun ====)", + "", + "Price: 400" + ] + + assert len(receipt_lines) == len(expected_lines) + for i, expected_line in enumerate(expected_lines): + assert receipt_lines[i] == expected_line + + # Тест на удаление ингредиента с несуществующим индексом + def test_remove_ingredient_invalid_index(self): + burger = Burger() + mock_ingredient = Mock(spec=Ingredient) + burger.add_ingredient(mock_ingredient) + + # Должен поднять IndexError при попытке удалить несуществующий индекс + with pytest.raises(IndexError): + burger.remove_ingredient(5) + + # Тест на перемещение ингредиента с несуществующим индексом + def test_move_ingredient_invalid_index(self): + burger = Burger() + mock_ingredient = Mock(spec=Ingredient) + burger.add_ingredient(mock_ingredient) + + # Сохраняем исходный список ингредиентов + original_ingredients = burger.ingredients.copy() + + # Проверяем, что при попытке переместить несуществующий индекс возникает ошибка + try: + burger.move_ingredient(5, 0) + # Если не возникла ошибка, тест должен упасть + assert False, "Expected IndexError but no exception was raised" + except IndexError as e: + # Проверяем, что список ингредиентов не изменился после ошибки + assert burger.ingredients == original_ingredients + # Можно также проверить текст ошибки, если нужно + assert "pop index out of range" in str(e) + + # Дополнительный тест для проверки преобразования типов ингредиентов в нижний регистр + def test_get_receipt_ingredient_types_lowercase(self): + burger = Burger() + + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = "test bun" + mock_bun.get_price.return_value = 0 + burger.set_buns(mock_bun) + + # Добавляем ингредиенты с разными типами + mock_sauce = Mock(spec=Ingredient) + mock_sauce.get_type.return_value = INGREDIENT_TYPE_SAUCE + mock_sauce.get_name.return_value = "test sauce" + mock_sauce.get_price.return_value = 0 + + mock_filling = Mock(spec=Ingredient) + mock_filling.get_type.return_value = INGREDIENT_TYPE_FILLING + mock_filling.get_name.return_value = "test filling" + mock_filling.get_price.return_value = 0 + + burger.add_ingredient(mock_sauce) + burger.add_ingredient(mock_filling) + + receipt = burger.get_receipt() + + # Проверяем, что типы ингредиентов преобразованы в нижний регистр + assert "= sauce test sauce =" in receipt + assert "= filling test filling =" in receipt \ No newline at end of file diff --git a/test_data.py b/test_data.py new file mode 100644 index 000000000..e9096f9d7 --- /dev/null +++ b/test_data.py @@ -0,0 +1,65 @@ +from ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING + +# Тестовые данные для get_receipt +GET_RECEIPT_TEST_DATA = [ + { + "name": "Бургер с соусом и котлетой", + "bun_name": "black bun", + "ingredients": [ + (INGREDIENT_TYPE_SAUCE, "hot sauce"), + (INGREDIENT_TYPE_FILLING, "cutlet") + ], + "expected_lines": [ + "(==== black bun ====)", + "= sauce hot sauce =", + "= filling cutlet =", + "(==== black bun ====)", + "", + "Price: 0" + ] + }, + { + "name": "Бургер с динозавром и сметаной", + "bun_name": "white bun", + "ingredients": [ + (INGREDIENT_TYPE_FILLING, "dinosaur"), + (INGREDIENT_TYPE_SAUCE, "sour cream") + ], + "expected_lines": [ + "(==== white bun ====)", + "= filling dinosaur =", + "= sauce sour cream =", + "(==== white bun ====)", + "", + "Price: 0" + ] + } +] + +# Тестовые данные для get_price +GET_PRICE_TEST_DATA = [ + { + "name": "Булочка за 100 и два ингредиента", + "bun_price": 100, + "ingredient_prices": [50, 75], + "expected_total": 325 + }, + { + "name": "Булочка за 50 и три ингредиента", + "bun_price": 50, + "ingredient_prices": [25, 30, 45], + "expected_total": 200 + }, + { + "name": "Только булочка", + "bun_price": 200, + "ingredient_prices": [], + "expected_total": 400 + }, + { + "name": "Булочка бесплатная с платными ингредиентами", + "bun_price": 0, + "ingredient_prices": [10, 20], + "expected_total": 30 + } +] \ No newline at end of file