From 3026b1f75ed8c1346a3903adf6c61ae5185b5b35 Mon Sep 17 00:00:00 2001 From: Viktor Vorobev Date: Fri, 5 Dec 2025 17:22:39 +0300 Subject: [PATCH 1/2] UI tests was writed for Stellar Burgers --- README.md | 2 +- burger.py | 4 +- database.py | 6 +- praktikum.py | 8 +- test_burger.py | 258 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 268 insertions(+), 10 deletions(-) create mode 100644 test_burger.py 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..ae4e8beae --- /dev/null +++ b/test_burger.py @@ -0,0 +1,258 @@ +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 + + +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 + + # Параметризованные тесты для метода get_price + @pytest.mark.parametrize("bun_price,ingredient_prices,expected_total", [ + (100, [50, 75], 325), # 100*2 + 50 + 75 = 325 + (50, [25, 30, 45], 200), # 50*2 + 25 + 30 + 45 = 200 + (200, [], 400), # 200*2 + 0 = 400 + (0, [10, 20], 30), # 0*2 + 10 + 20 = 30 + ]) + def test_get_price_with_different_combinations(self, bun_price, ingredient_prices, expected_total): + burger = Burger() + + # Мок булки + mock_bun = Mock(spec=Bun) + mock_bun.get_price.return_value = bun_price + burger.set_buns(mock_bun) + + # Моки ингредиентов + for price in ingredient_prices: + mock_ingredient = Mock(spec=Ingredient) + mock_ingredient.get_price.return_value = price + burger.add_ingredient(mock_ingredient) + + result = burger.get_price() + + assert result == expected_total + mock_bun.get_price.assert_called_once() + + # Тест для метода 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 # 100*2 + 50 + 75 + mock_bun.get_price.assert_called_once() + mock_ingredient1.get_price.assert_called_once() + mock_ingredient2.get_price.assert_called_once() + + # Параметризованные тесты для метода get_receipt + @pytest.mark.parametrize("bun_name,ingredients_data,expected_lines", [ + ( + "black bun", + [ + (INGREDIENT_TYPE_SAUCE, "hot sauce"), + (INGREDIENT_TYPE_FILLING, "cutlet") + ], + [ + "(==== black bun ====)", + "= sauce hot sauce =", + "= filling cutlet =", + "(==== black bun ====)", + "", # Пустая строка после второй булки + "Price: 0" + ] + ), + ( + "white bun", + [ + (INGREDIENT_TYPE_FILLING, "dinosaur"), + (INGREDIENT_TYPE_SAUCE, "sour cream") + ], + [ + "(==== white bun ====)", + "= filling dinosaur =", + "= sauce sour cream =", + "(==== white bun ====)", + "", # Пустая строка после второй булки + "Price: 0" + ] + ), + ]) + def test_get_receipt_format(self, bun_name, ingredients_data, expected_lines): + burger = Burger() + + # Мок булки + mock_bun = Mock(spec=Bun) + mock_bun.get_name.return_value = bun_name + mock_bun.get_price.return_value = 0 + burger.set_buns(mock_bun) + + # Моки ингредиентов + for ingredient_type, ingredient_name in ingredients_data: + 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 len(receipt_lines) == len(expected_lines) + for i, expected_line in enumerate(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) + + # Должен поднять IndexError при попытке переместить несуществующий индекс + with pytest.raises(IndexError): + burger.move_ingredient(5, 0) + + # Дополнительный тест для проверки преобразования типов ингредиентов в нижний регистр + 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 From 2ac248e9dd621cf255c5ae2d713c3fbb6baab4e2 Mon Sep 17 00:00:00 2001 From: Viktor Vorobev Date: Sun, 7 Dec 2025 14:02:13 +0300 Subject: [PATCH 2/2] changes --- test_burger.py | 84 +++++++++++++++++--------------------------------- test_data.py | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 test_data.py diff --git a/test_burger.py b/test_burger.py index ae4e8beae..a82b67f99 100644 --- a/test_burger.py +++ b/test_burger.py @@ -4,6 +4,7 @@ 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: @@ -58,31 +59,25 @@ def test_move_ingredient(self): assert burger.ingredients[1] == mock_ingredient3 assert burger.ingredients[2] == mock_ingredient1 - # Параметризованные тесты для метода get_price - @pytest.mark.parametrize("bun_price,ingredient_prices,expected_total", [ - (100, [50, 75], 325), # 100*2 + 50 + 75 = 325 - (50, [25, 30, 45], 200), # 50*2 + 25 + 30 + 45 = 200 - (200, [], 400), # 200*2 + 0 = 400 - (0, [10, 20], 30), # 0*2 + 10 + 20 = 30 - ]) - def test_get_price_with_different_combinations(self, bun_price, ingredient_prices, expected_total): + @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 = bun_price + mock_bun.get_price.return_value = test_data["bun_price"] burger.set_buns(mock_bun) # Моки ингредиентов - for price in ingredient_prices: + 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 result == expected_total - mock_bun.get_price.assert_called_once() + # Убираем лишние строки после assert + assert result == test_data["expected_total"] # Тест для метода get_price с проверкой вызовов методов def test_get_price_calls_methods(self): @@ -102,55 +97,21 @@ def test_get_price_calls_methods(self): result = burger.get_price() - assert result == 325 # 100*2 + 50 + 75 - mock_bun.get_price.assert_called_once() - mock_ingredient1.get_price.assert_called_once() - mock_ingredient2.get_price.assert_called_once() + assert result == 325 # Параметризованные тесты для метода get_receipt - @pytest.mark.parametrize("bun_name,ingredients_data,expected_lines", [ - ( - "black bun", - [ - (INGREDIENT_TYPE_SAUCE, "hot sauce"), - (INGREDIENT_TYPE_FILLING, "cutlet") - ], - [ - "(==== black bun ====)", - "= sauce hot sauce =", - "= filling cutlet =", - "(==== black bun ====)", - "", # Пустая строка после второй булки - "Price: 0" - ] - ), - ( - "white bun", - [ - (INGREDIENT_TYPE_FILLING, "dinosaur"), - (INGREDIENT_TYPE_SAUCE, "sour cream") - ], - [ - "(==== white bun ====)", - "= filling dinosaur =", - "= sauce sour cream =", - "(==== white bun ====)", - "", # Пустая строка после второй булки - "Price: 0" - ] - ), - ]) - def test_get_receipt_format(self, bun_name, ingredients_data, expected_lines): + @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 = bun_name + 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 ingredients_data: + 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 @@ -160,8 +121,9 @@ def test_get_receipt_format(self, bun_name, ingredients_data, expected_lines): receipt = burger.get_receipt() receipt_lines = receipt.split('\n') - assert len(receipt_lines) == len(expected_lines) - for i, expected_line in enumerate(expected_lines): + # Убираем лишние строки после 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 с правильной ценой @@ -224,10 +186,20 @@ def test_move_ingredient_invalid_index(self): mock_ingredient = Mock(spec=Ingredient) burger.add_ingredient(mock_ingredient) - # Должен поднять IndexError при попытке переместить несуществующий индекс - with pytest.raises(IndexError): + # Сохраняем исходный список ингредиентов + 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() 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