Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Виртуальное окружение
venv/
env/
.env/
.venv/

# Кэши Python
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/

# Отчёты о покрытии
htmlcov/
.coverage
.coverage.*
*.cover
*.py,cover

# Файлы IDE (VS Code, PyCharm и др.)
.vscode/
.idea/
*.swp
*.swo
*~

# Логи и временные файлы
*.log
*.tmp
*.temp

# Системные файлы (Windows)
Thumbs.db
ehthumbs.db
Desktop.ini

# Файлы с секретами (если появятся)
.env
*.key
*.pem
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file added requirements.txt
Empty file.
Empty file added tests/__init__.py
Empty file.
122 changes: 122 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import pytest
from unittest.mock import create_autospec

from praktikum.bun import Bun
from praktikum.ingredient import Ingredient
from praktikum.database import Database
from praktikum.burger import Burger
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING


# -------------------------------------------------------------------
# Фикстуры для конкретных объектов (реальные или моки)
# -------------------------------------------------------------------

@pytest.fixture
def black_bun():
"""Реальная булочка black bun."""
return Bun("black bun", 100)


@pytest.fixture
def white_bun():
"""Реальная булочка white bun."""
return Bun("white bun", 200)


@pytest.fixture
def red_bun():
"""Реальная булочка red bun."""
return Bun("red bun", 300)


@pytest.fixture
def hot_sauce():
"""Реальный соус hot sauce."""
return Ingredient(INGREDIENT_TYPE_SAUCE, "hot sauce", 100)


@pytest.fixture
def sour_cream():
"""Реальный соус sour cream."""
return Ingredient(INGREDIENT_TYPE_SAUCE, "sour cream", 200)


@pytest.fixture
def chili_sauce():
"""Реальный соус chili sauce."""
return Ingredient(INGREDIENT_TYPE_SAUCE, "chili sauce", 300)


@pytest.fixture
def cutlet():
"""Реальная начинка cutlet."""
return Ingredient(INGREDIENT_TYPE_FILLING, "cutlet", 100)


@pytest.fixture
def dinosaur():
"""Реальная начинка dinosaur."""
return Ingredient(INGREDIENT_TYPE_FILLING, "dinosaur", 200)


@pytest.fixture
def sausage():
"""Реальная начинка sausage."""
return Ingredient(INGREDIENT_TYPE_FILLING, "sausage", 300)


# -------------------------------------------------------------------
# Фикстуры для моков (используются в тестах Burger)
# -------------------------------------------------------------------

@pytest.fixture
def mock_bun():
"""Мок булочки с предопределёнными значениями."""
bun = create_autospec(Bun, instance=True)
bun.get_name.return_value = "black bun"
bun.get_price.return_value = 100
return bun


@pytest.fixture
def mock_ingredient_sauce():
"""Мок соуса."""
ing = create_autospec(Ingredient, instance=True)
ing.get_type.return_value = INGREDIENT_TYPE_SAUCE
ing.get_name.return_value = "hot sauce"
ing.get_price.return_value = 200
return ing


@pytest.fixture
def mock_ingredient_filling():
"""Мок начинки."""
ing = create_autospec(Ingredient, instance=True)
ing.get_type.return_value = INGREDIENT_TYPE_FILLING
ing.get_name.return_value = "cutlet"
ing.get_price.return_value = 300
return ing


# -------------------------------------------------------------------
# Фикстуры для сложных объектов
# -------------------------------------------------------------------

@pytest.fixture
def database():
"""Экземпляр базы данных."""
return Database()


@pytest.fixture
def burger():
"""Пустой бургер."""
return Burger()


@pytest.fixture
def burger_with_bun(burger, mock_bun):
"""Бургер с установленной булкой (мок)."""
burger.set_buns(mock_bun)
return burger
23 changes: 23 additions & 0 deletions tests/test_bun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import pytest
from praktikum.bun import Bun


class TestBun:

@pytest.mark.parametrize("name, price", [
("black bun", 100),
("white bun", 200),
("red bun", 300),
("", 0),
("super bun", 99.9)
])
def test_bun_creation(self, name, price):
bun = Bun(name, price)
assert bun.name == name
assert bun.price == price

def test_get_name(self, black_bun):
assert black_bun.get_name() == "black bun"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно лучше: во всех тестах лучше выносить тестовые данные во внешний модуль (например, data). Это позволит облегчить их поддержку


def test_get_price(self, black_bun):
assert black_bun.get_price() == 100
98 changes: 98 additions & 0 deletions tests/test_burger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import pytest
from unittest.mock import Mock

from praktikum.burger import Burger
from praktikum.bun import Bun
from praktikum.ingredient import Ingredient


class TestBurger:

def test_set_buns(self, burger, mock_bun):
burger.set_buns(mock_bun)
assert burger.bun == mock_bun

def test_add_ingredient(self, burger, mock_ingredient_sauce):
burger.add_ingredient(mock_ingredient_sauce)
assert len(burger.ingredients) == 1
assert burger.ingredients[0] == mock_ingredient_sauce

def test_remove_ingredient(self, burger):
ing1, ing2 = Mock(spec=Ingredient), Mock(spec=Ingredient)
burger.ingredients = [ing1, ing2]
burger.remove_ingredient(0)
assert burger.ingredients == [ing2]

def test_remove_ingredient_invalid_index_raises_error(self, burger):
burger.ingredients = [Mock(spec=Ingredient)]
with pytest.raises(IndexError):
burger.remove_ingredient(5)

def test_move_ingredient(self, burger):
ing1, ing2, ing3 = Mock(spec=Ingredient), Mock(spec=Ingredient), Mock(spec=Ingredient)
burger.ingredients = [ing1, ing2, ing3]
burger.move_ingredient(0, 2)
assert burger.ingredients == [ing2, ing3, ing1]

def test_move_ingredient_invalid_source_index_raises_error(self, burger):
burger.ingredients = [Mock(spec=Ingredient), Mock(spec=Ingredient)]
with pytest.raises(IndexError):
burger.move_ingredient(5, 0) # неверный исходный индекс

def test_move_ingredient_valid_source_with_invalid_target_does_not_raise(self, burger):
burger.ingredients = [Mock(spec=Ingredient), Mock(spec=Ingredient)]
# Не должно быть исключения, т.к. insert при выходе за границы просто добавляет в конец
burger.move_ingredient(0, 5)

@pytest.mark.parametrize(
"bun_price, ingredient_prices, expected_price",
[
(50, [10, 20], 130.0),
(100, [], 200.0),
(30, [15, 25, 35], 135.0),
(0, [10, 10], 20.0),
(50.5, [10.2, 20.3], 131.5),
]
)
def test_get_price(self, burger, bun_price, ingredient_prices, expected_price):
mock_bun = Mock(spec=Bun)
mock_bun.get_price.return_value = bun_price
burger.set_buns(mock_bun)

for price in ingredient_prices:
mock_ing = Mock(spec=Ingredient)
mock_ing.get_price.return_value = price
burger.add_ingredient(mock_ing)

assert burger.get_price() == expected_price

def test_get_receipt(self, burger_with_bun, mock_ingredient_sauce, mock_ingredient_filling):
burger_with_bun.add_ingredient(mock_ingredient_sauce)
burger_with_bun.add_ingredient(mock_ingredient_filling)

expected_receipt = (
"(==== black bun ====)\n"
"= sauce hot sauce =\n"
"= filling cutlet =\n"
"(==== black bun ====)\n"
"\n"
"Price: 700" # убрали .0
)
assert burger_with_bun.get_receipt() == expected_receipt

def test_receipt_without_ingredients(self, burger_with_bun):
expected_receipt = (
"(==== black bun ====)\n"
"(==== black bun ====)\n"
"\n"
"Price: 200" # убрали .0
)
assert burger_with_bun.get_receipt() == expected_receipt

def test_price_without_bun_raises_error(self, burger):
with pytest.raises(AttributeError):
burger.get_price()

def test_receipt_without_bun_raises_error(self, burger):
with pytest.raises(AttributeError):
burger.get_receipt()
58 changes: 58 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from praktikum.bun import Bun
from praktikum.ingredient import Ingredient
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING


class TestDatabase:

def test_initial_buns_count(self, database):
buns = database.available_buns()
assert len(buns) == 3

def test_initial_ingredients_count(self, database):
ingredients = database.available_ingredients()
assert len(ingredients) == 6 # 3 соуса + 3 начинки

def test_buns_content(self, database, black_bun, white_bun, red_bun):
buns = database.available_buns()
# Проверяем через сравнение атрибутов, так как это разные объекты
assert buns[0].get_name() == black_bun.get_name()
assert buns[0].get_price() == black_bun.get_price()
assert buns[1].get_name() == white_bun.get_name()
assert buns[1].get_price() == white_bun.get_price()
assert buns[2].get_name() == red_bun.get_name()
assert buns[2].get_price() == red_bun.get_price()

def test_ingredients_content(self, database, hot_sauce, sour_cream, chili_sauce,
cutlet, dinosaur, sausage):
ingredients = database.available_ingredients()
# Соусы
assert ingredients[0].get_type() == hot_sauce.get_type()
assert ingredients[0].get_name() == hot_sauce.get_name()
assert ingredients[0].get_price() == hot_sauce.get_price()
assert ingredients[1].get_type() == sour_cream.get_type()
assert ingredients[1].get_name() == sour_cream.get_name()
assert ingredients[1].get_price() == sour_cream.get_price()
assert ingredients[2].get_type() == chili_sauce.get_type()
assert ingredients[2].get_name() == chili_sauce.get_name()
assert ingredients[2].get_price() == chili_sauce.get_price()
# Начинки
assert ingredients[3].get_type() == cutlet.get_type()
assert ingredients[3].get_name() == cutlet.get_name()
assert ingredients[3].get_price() == cutlet.get_price()
assert ingredients[4].get_type() == dinosaur.get_type()
assert ingredients[4].get_name() == dinosaur.get_name()
assert ingredients[4].get_price() == dinosaur.get_price()
assert ingredients[5].get_type() == sausage.get_type()
assert ingredients[5].get_name() == sausage.get_name()
assert ingredients[5].get_price() == sausage.get_price()

def test_available_buns_returns_same_list(self, database):
buns1 = database.available_buns()
buns2 = database.available_buns()
assert buns1 is buns2

def test_available_ingredients_returns_same_list(self, database):
ing1 = database.available_ingredients()
ing2 = database.available_ingredients()
assert ing1 is ing2
28 changes: 28 additions & 0 deletions tests/test_ingredient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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_SAUCE, "sour cream", 200),
(INGREDIENT_TYPE_FILLING, "cutlet", 100),
(INGREDIENT_TYPE_FILLING, "dinosaur", 200),
("UNKNOWN", "strange", 0),
])
def test_ingredient_creation(self, ingredient_type, name, price):
ingredient = Ingredient(ingredient_type, name, price)
assert ingredient.type == ingredient_type
assert ingredient.name == name
assert ingredient.price == price

def test_get_price(self, hot_sauce):
assert hot_sauce.get_price() == 100

def test_get_name(self, hot_sauce):
assert hot_sauce.get_name() == "hot sauce"

def test_get_type(self, cutlet):
assert cutlet.get_type() == INGREDIENT_TYPE_FILLING