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
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# =========================
# Python
# =========================
__pycache__/
*.py[cod]
*.pyo

# =========================
# Pytest
# =========================
.pytest_cache/

# Игнорируем всё остальное, кроме htmlcov
.coverage
htmlcov/*
!htmlcov/index.html
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@

Созданы юнит-тесты, покрывающие классы `Bun`, `Burger`, `Ingredient`, `Database`

Процент покрытия 100% (отчет: `htmlcov/index.html`)
Процент покрытия 100% (без main()) (отчет: `htmlcov/index.html`)

### Структура проекта

- `praktikum` - пакет, содержащий код программы
- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д.

## Тесты

Тесты покрывают все классы и используют:
- `pytest` для запуска
- `pytest-mock` для моков объектов (`Bun`, `Ingredient`)
- параметризацию (`@pytest.mark.parametrize`) для тестирования нескольких сценариев


### Запуск автотестов

**Установка зависимостей**
Expand Down
72 changes: 72 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import pytest
from praktikum.bun import Bun
from praktikum.ingredient import Ingredient
from praktikum.burger import Burger
from praktikum.database import Database
from test_data import *
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE
from unittest.mock import Mock


@pytest.fixture
def bun():
return Bun(BUN_NAME, BUN_PRICE)

@pytest.fixture
def ingredient_factory():
def _create(ingredient_type, name, price):
return Ingredient(ingredient_type, name, price)
return _create

@pytest.fixture
def sauce_ingredient(ingredient_factory):
return ingredient_factory(
INGREDIENT_TYPE_SAUCE,
INGREDIENT_NAME,
INGREDIENT_PRICE
)

@pytest.fixture
def filling_ingredient(ingredient_factory):
return ingredient_factory(INGREDIENT_TYPE_FILLING, FILLING_NAME, FILLING_PRICE)

@pytest.fixture
def burger() -> Burger:
return Burger()

@pytest.fixture
def db() -> Database:
return Database()

@pytest.fixture
def mock_bun():
bun = Mock()
bun.get_name.return_value = MOCK_BUN_NAME
bun.get_price.return_value = MOCK_BUN_PRICE
return bun

@pytest.fixture
def mock_sauce():
ingredient = Mock()
ingredient.get_type.return_value = INGREDIENT_TYPE_SAUCE
ingredient.get_name.return_value = SAUCE_NAME
ingredient.get_price.return_value = SAUCE_PRICE
return ingredient

@pytest.fixture
def mock_filling():
ingredient = Mock()
ingredient.get_type.return_value = INGREDIENT_TYPE_FILLING
ingredient.get_name.return_value = FILLING_NAME
ingredient.get_price.return_value = FILLING_PRICE
return ingredient

@pytest.fixture
def mock_ingredient_factory():
def _create(ingredient_type, name, price):
ingredient = Mock(spec=["get_type", "get_name", "get_price"])
ingredient.get_type.return_value = ingredient_type
ingredient.get_name.return_value = name
ingredient.get_price.return_value = price
return ingredient
return _create
Empty file added praktikum/__init__.py‎
Empty file.
File renamed without changes.
1 change: 1 addition & 0 deletions burger.py → praktikum/burger.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Burger:
Можно распечать чек с информацией о бургере.
"""


def __init__(self):
self.bun = None
self.ingredients: List[Ingredient] = []
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
27 changes: 27 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
allure-pytest==2.15.3
allure-python-commons==2.15.3
attrs==25.4.0
certifi==2026.1.4
charset-normalizer==3.4.4
coverage==7.13.0
Faker==40.1.2
h11==0.16.0
idna==3.11
iniconfig==2.3.0
outcome==1.3.0.post0
packaging==25.0
pluggy==1.6.0
Pygments==2.19.2
PySocks==1.7.1
pytest==9.0.2
pytest-cov==7.0.0
requests==2.32.5
selenium==4.39.0
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.32.0
trio-websocket==0.12.2
typing_extensions==4.15.0
urllib3==2.6.3
websocket-client==1.9.0
wsproto==1.3.2
26 changes: 26 additions & 0 deletions test_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from praktikum.ingredient_types import INGREDIENT_TYPE_FILLING

BUN_NAME = "Космическая булочка"
BUN_PRICE = 1150.0

INGREDIENT_TYPE = "SAUCE"
INGREDIENT_NAME = "Соус Spicy-X"
INGREDIENT_PRICE = 888.8

SAUCE_NAME = "Соус с шипами Антарианского плоскоходца"
SAUCE_PRICE = 88

SAUCE_NAME_2 = "Соус фирменный Space Sauce"
SAUCE_PRICE_2 = 13

FILLING_NAME = "Биокотлета из марсианской Магнолии"
FILLING_PRICE = 424

# Mock Bun
MOCK_BUN_NAME = "Космическая булочка (поджаренная)"
MOCK_BUN_PRICE = 431.00

# Mock Ingredient
MOCK_INGREDIENT_TYPE = INGREDIENT_TYPE_FILLING
MOCK_INGREDIENT_NAME = "Биокотлета из марсианской Магнолии (роскошная)"
MOCK_INGREDIENT_PRICE = 666.00
Empty file added tests/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions tests/test_bun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from praktikum.bun import Bun
from test_data import BUN_NAME, BUN_PRICE

class TestBun:

def test_get_name_returns_correct_name(self, bun: Bun):
assert bun.get_name() == BUN_NAME

def test_get_price_returns_correct_price(self, bun: Bun):
assert bun.get_price() == BUN_PRICE
88 changes: 88 additions & 0 deletions tests/test_burger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from praktikum.burger import Burger
from test_data import *
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING
import pytest


class TestBurger:

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

def test_add_single_ingredient(self, burger: Burger, mock_sauce):
burger.add_ingredient(mock_sauce)
assert burger.ingredients == [mock_sauce]

def test_remove_sauce_ingredient(self, burger: Burger, mock_sauce):
burger.add_ingredient(mock_sauce)
burger.remove_ingredient(0)
assert burger.ingredients == []

def test_move_single_ingredient_no_change(self, burger: Burger, mock_sauce):
burger.add_ingredient(mock_sauce)

burger.move_ingredient(0, 0)
assert burger.ingredients == [mock_sauce]

@pytest.mark.parametrize(
"start_index, new_index, expected_order",
[
(0, 2, ["FILLING", "SAUCE", "SAUCE"]),
(2, 0, ["SAUCE", "SAUCE", "FILLING"]),
(1, 1, ["SAUCE", "FILLING", "SAUCE"]),
]
)
def test_move_ingredient_various_positions(
self,
burger: Burger,
mock_ingredient_factory,
start_index,
new_index,
expected_order
):
ing1 = mock_ingredient_factory(INGREDIENT_TYPE_SAUCE, SAUCE_NAME, SAUCE_PRICE)
ing2 = mock_ingredient_factory(INGREDIENT_TYPE_FILLING, FILLING_NAME, FILLING_PRICE)
ing3 = mock_ingredient_factory(INGREDIENT_TYPE_SAUCE, SAUCE_NAME_2, SAUCE_PRICE_2)

for ing in [ing1, ing2, ing3]:
burger.add_ingredient(ing)

burger.move_ingredient(start_index, new_index)

remaining_types = [i.get_type() for i in burger.ingredients]
assert remaining_types == expected_order

def test_get_price_single_ingredient(self, burger: Burger, mock_bun, mock_sauce):

burger.set_buns(mock_bun)
burger.add_ingredient(mock_sauce)

expected_price = mock_bun.get_price() * 2 + mock_sauce.get_price()

assert burger.get_price() == expected_price

def test_burger_get_price_multiple_ingredients(self, burger: Burger, mock_bun, mock_sauce, mock_filling):
burger.set_buns(mock_bun)

burger.add_ingredient(mock_sauce)
burger.add_ingredient(mock_filling)

expected_price = mock_bun.get_price() * 2 + mock_sauce.get_price() + mock_filling.get_price()
assert burger.get_price() == expected_price

def test_get_receipt_single_ingredient1(self, burger: Burger, mock_bun, mock_sauce):
burger.set_buns(mock_bun)
burger.add_ingredient(mock_sauce)

receipt = burger.get_receipt()

expected_lines = [
f"(==== {MOCK_BUN_NAME} ====)",
f"= sauce {SAUCE_NAME} =",
f"(==== {MOCK_BUN_NAME} ====)",
"",
f"Price: {burger.get_price()}"
]

assert receipt.split("\n") == expected_lines
32 changes: 32 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from praktikum.database import Database


class TestDatabase:

def test_database_buns_returns_all_buns(self, db: Database, mock_bun):

db.buns = [mock_bun, mock_bun, mock_bun]

buns = db.available_buns()

assert len(buns) == 3
assert buns[0].get_name() == mock_bun.get_name()
assert buns[0].get_price() == mock_bun.get_price()

def test_available_ingredients(self, db: Database, mock_sauce, mock_filling):

db.ingredients = [mock_sauce, mock_filling]

ingredients = db.available_ingredients()

assert len(ingredients) == 2
assert ingredients[0] is mock_sauce
assert ingredients[0].get_type() == mock_sauce.get_type()
assert ingredients[0].get_name() == mock_sauce.get_name()
assert ingredients[0].get_price() == mock_sauce.get_price()

assert ingredients[1] is mock_filling
assert ingredients[1].get_type() == mock_filling.get_type()
assert ingredients[1].get_name() == mock_filling.get_name()
assert ingredients[1].get_price() == mock_filling.get_price()

40 changes: 40 additions & 0 deletions tests/test_ingredient.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from praktikum.ingredient import Ingredient
from test_data import INGREDIENT_NAME, INGREDIENT_PRICE, FILLING_NAME, FILLING_PRICE, SAUCE_NAME, SAUCE_PRICE
from praktikum.ingredient_types import INGREDIENT_TYPE_SAUCE, INGREDIENT_TYPE_FILLING
import pytest


class TestIngredient:

def test_get_name_returns_correct_name(self, sauce_ingredient: Ingredient):
assert sauce_ingredient.get_name() == INGREDIENT_NAME

def test_get_price_returns_correct_price(self, sauce_ingredient: Ingredient):
assert sauce_ingredient.get_price() == INGREDIENT_PRICE

def test_get_type_returns_sauce_type(self, sauce_ingredient: Ingredient):
assert sauce_ingredient.get_type() == INGREDIENT_TYPE_SAUCE

def test_get_type_returns_filling_type(self, filling_ingredient: Ingredient):
assert filling_ingredient.get_type() == INGREDIENT_TYPE_FILLING

@pytest.mark.parametrize(
"ingredient_type, name, price, expected_type",
[
(INGREDIENT_TYPE_SAUCE, SAUCE_NAME, SAUCE_PRICE, INGREDIENT_TYPE_SAUCE),
(INGREDIENT_TYPE_FILLING, FILLING_NAME, FILLING_PRICE, INGREDIENT_TYPE_FILLING),
]
)
def test_ingredient_properties(
self,
ingredient_factory,
ingredient_type,
name,
price,
expected_type
):
ingredient = ingredient_factory(ingredient_type, name, price)

assert ingredient.get_name() == name
assert ingredient.get_price() == price
assert ingredient.get_type() == expected_type