diff --git a/.coverage b/.coverage new file mode 100644 index 000000000..65a74b9a0 Binary files /dev/null and b/.coverage differ diff --git a/README.md b/README.md index 272081708..4f63b4e21 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,15 @@ ### Структура проекта -- `praktikum` - пакет, содержащий код программы -- `tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. +`praktikum` - пакет, содержащий код программы +`tests` - пакет, содержащий тесты, разделенные по классам. Например, `bun_test.py`, `burger_test.py` и т.д. ### Запуск автотестов **Установка зависимостей** -> `$ pip install -r requirements.txt` +`$ pip install -r requirements.txt` **Запуск автотестов и создание HTML-отчета о покрытии** -> `$ pytest --cov=praktikum --cov-report=html` +`$ pytest --cov=praktikum --cov-report=html` diff --git a/__pycache__/__init__.cpython-314.pyc b/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 000000000..64ebc3567 Binary files /dev/null and b/__pycache__/__init__.cpython-314.pyc differ diff --git a/__pycache__/bun.cpython-314.pyc b/__pycache__/bun.cpython-314.pyc new file mode 100644 index 000000000..49bb0ace9 Binary files /dev/null and b/__pycache__/bun.cpython-314.pyc differ diff --git a/__pycache__/burger.cpython-314.pyc b/__pycache__/burger.cpython-314.pyc new file mode 100644 index 000000000..028e000a1 Binary files /dev/null and b/__pycache__/burger.cpython-314.pyc differ diff --git a/__pycache__/database.cpython-314.pyc b/__pycache__/database.cpython-314.pyc new file mode 100644 index 000000000..f60dd65eb Binary files /dev/null and b/__pycache__/database.cpython-314.pyc differ diff --git a/__pycache__/ingredient.cpython-314.pyc b/__pycache__/ingredient.cpython-314.pyc new file mode 100644 index 000000000..ce084de71 Binary files /dev/null and b/__pycache__/ingredient.cpython-314.pyc differ diff --git a/__pycache__/ingredient_types.cpython-314.pyc b/__pycache__/ingredient_types.cpython-314.pyc new file mode 100644 index 000000000..2e67fc3a4 Binary files /dev/null and b/__pycache__/ingredient_types.cpython-314.pyc differ diff --git a/__pycache__/praktikum.cpython-314.pyc b/__pycache__/praktikum.cpython-314.pyc new file mode 100644 index 000000000..a2f90e5e4 Binary files /dev/null and b/__pycache__/praktikum.cpython-314.pyc differ 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/coverage.xml b/coverage.xml new file mode 100644 index 000000000..725ea40d2 --- /dev/null +++ b/coverage.xml @@ -0,0 +1,133 @@ + + + + + + /Users/tim/Diplom/qa-python-project + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/pytest.ini b/pytest.ini new file mode 100644 index 000000000..a3a8dabe0 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +addopts = --cov=bun --cov=burger --cov=database --cov=ingredient --cov=ingredient_types --cov=praktikum --cov-report=term-missing --cov-report=xml +python_files = tests/test_*.py diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..772247572 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest>=8.0 +pytest-cov>=4.0 diff --git a/tests/__pycache__/test_burger.cpython-314-pytest-8.3.2.pyc b/tests/__pycache__/test_burger.cpython-314-pytest-8.3.2.pyc new file mode 100644 index 000000000..410c16e80 Binary files /dev/null and b/tests/__pycache__/test_burger.cpython-314-pytest-8.3.2.pyc differ diff --git a/tests/__pycache__/test_burger.cpython-314-pytest-9.1.1.pyc b/tests/__pycache__/test_burger.cpython-314-pytest-9.1.1.pyc new file mode 100644 index 000000000..7567ca4ad Binary files /dev/null and b/tests/__pycache__/test_burger.cpython-314-pytest-9.1.1.pyc differ diff --git a/tests/__pycache__/test_database.cpython-314-pytest-9.1.1.pyc b/tests/__pycache__/test_database.cpython-314-pytest-9.1.1.pyc new file mode 100644 index 000000000..67dc294fe Binary files /dev/null and b/tests/__pycache__/test_database.cpython-314-pytest-9.1.1.pyc differ diff --git a/tests/__pycache__/test_models.cpython-314-pytest-9.1.1.pyc b/tests/__pycache__/test_models.cpython-314-pytest-9.1.1.pyc new file mode 100644 index 000000000..66ff380e4 Binary files /dev/null and b/tests/__pycache__/test_models.cpython-314-pytest-9.1.1.pyc differ diff --git a/tests/__pycache__/test_praktikum.cpython-314-pytest-9.1.1.pyc b/tests/__pycache__/test_praktikum.cpython-314-pytest-9.1.1.pyc new file mode 100644 index 000000000..5338ca522 Binary files /dev/null and b/tests/__pycache__/test_praktikum.cpython-314-pytest-9.1.1.pyc differ diff --git a/tests/test_burger.py b/tests/test_burger.py new file mode 100644 index 000000000..03fe0f8a6 --- /dev/null +++ b/tests/test_burger.py @@ -0,0 +1,102 @@ +import pytest +from unittest.mock import Mock + +from burger import Burger +from ingredient import Ingredient + + +def test_burger_init_sets_empty_state(): + burger = Burger() + + assert burger.bun is None + assert burger.ingredients == [] + + +def test_set_buns_assigns_bun_object(): + bun = Mock(get_name=Mock(return_value="plain bun"), get_price=Mock(return_value=100.0)) + burger = Burger() + + burger.set_buns(bun) + + assert burger.bun is bun + + +def test_add_ingredient_appends_ingredient_to_list(): + ingredient = Ingredient("FILLING", "cutlet", 35.0) + burger = Burger() + + burger.add_ingredient(ingredient) + + assert burger.ingredients == [ingredient] + + +def test_remove_ingredient_deletes_item_by_index(): + first = Ingredient("SAUCE", "hot sauce", 10.0) + second = Ingredient("FILLING", "cutlet", 35.0) + burger = Burger() + burger.add_ingredient(first) + burger.add_ingredient(second) + + burger.remove_ingredient(0) + + assert burger.ingredients == [second] + + +@pytest.mark.parametrize( + "ingredient_prices,expected_total", + [ + ([100.0], 300.0), + ([10.0, 20.0], 230.0), + ([5.0, 15.0, 25.0], 245.0), + ], +) +def test_get_price_includes_bun_twice_and_all_ingredient_prices( + ingredient_prices, expected_total +): + # Мокаем булочку и ингредиенты, чтобы тест был независим от реализации Ingredient. + bun = Mock(get_name=Mock(return_value="mock bun"), get_price=Mock(return_value=100.0)) + burger = Burger() + burger.set_buns(bun) + + for price in ingredient_prices: + ingredient = Mock(get_price=Mock(return_value=price)) + burger.add_ingredient(ingredient) + + assert burger.get_price() == expected_total + + +@pytest.mark.parametrize( + "initial_names,move_from,move_to,expected_names", + [ + (["hot sauce", "cutlet", "sausage"], 2, 0, ["sausage", "hot sauce", "cutlet"]), + (["sour cream", "dinosaur", "cutlet"], 0, 2, ["dinosaur", "cutlet", "sour cream"]), + ], +) +def test_move_ingredient_reorders_list_correctly( + initial_names, move_from, move_to, expected_names +): + burger = Burger() + + for name in initial_names: + burger.add_ingredient(Ingredient("FILLING", name, 1.0)) + + burger.move_ingredient(move_from, move_to) + assert [ingredient.get_name() for ingredient in burger.ingredients] == expected_names + + +def test_get_receipt_outputs_bun_and_ingredient_lines(): + bun = Mock(get_name=Mock(return_value="test bun"), get_price=Mock(return_value=50.0)) + ingredient = Mock( + get_type=Mock(return_value="SAUCE"), + get_name=Mock(return_value="hot sauce"), + get_price=Mock(return_value=10.0), + ) + burger = Burger() + burger.set_buns(bun) + burger.add_ingredient(ingredient) + + receipt = burger.get_receipt() + + assert "(==== test bun ====)" in receipt + assert "= sauce hot sauce =" in receipt + assert "Price: 110.0" in receipt diff --git a/tests/test_database.py b/tests/test_database.py new file mode 100644 index 000000000..afab86de7 --- /dev/null +++ b/tests/test_database.py @@ -0,0 +1,45 @@ +from database import Database + + +def test_available_buns_returns_three_items(): + database = Database() + buns = database.available_buns() + + assert len(buns) == 3 + + +def test_available_buns_returns_expected_bun_names(): + database = Database() + buns = database.available_buns() + + assert buns[0].get_name() == "black bun" + assert buns[1].get_name() == "white bun" + assert buns[2].get_name() == "red bun" + + +def test_available_buns_contains_expected_bun_price(): + database = Database() + buns = database.available_buns() + + assert buns[1].get_price() == 200 + + +def test_available_ingredients_returns_six_items(): + database = Database() + ingredients = database.available_ingredients() + + assert len(ingredients) == 6 + + +def test_available_ingredients_returns_sauce_and_filling_types(): + database = Database() + ingredients = database.available_ingredients() + + assert {ingredient.get_type() for ingredient in ingredients} == {"SAUCE", "FILLING"} + + +def test_available_ingredients_contains_cutlet(): + database = Database() + ingredients = database.available_ingredients() + + assert any(ingredient.get_name() == "cutlet" for ingredient in ingredients) diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 000000000..b1d35229c --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,76 @@ +import pytest + +from bun import Bun +from ingredient import Ingredient +from ingredient_types import INGREDIENT_TYPE_FILLING, INGREDIENT_TYPE_SAUCE + + +@pytest.mark.parametrize( + "name,price", + [ + ("sesame bun", 45.0), + ("plain bun", 80.5), + ], +) +def test_bun_get_name_returns_name(name: str, price: float): + bun = Bun(name, price) + + assert bun.get_name() == name + + +@pytest.mark.parametrize( + "name,price", + [ + ("sesame bun", 45.0), + ("plain bun", 80.5), + ], +) +def test_bun_get_price_returns_price(name: str, price: float): + bun = Bun(name, price) + + assert bun.get_price() == price + + +@pytest.mark.parametrize( + "ingredient_type,name,price", + [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 10.0), + (INGREDIENT_TYPE_FILLING, "cutlet", 35.5), + ], +) +def test_ingredient_get_type_returns_type( + ingredient_type: str, name: str, price: float +): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_type() == ingredient_type + + +@pytest.mark.parametrize( + "ingredient_type,name,price", + [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 10.0), + (INGREDIENT_TYPE_FILLING, "cutlet", 35.5), + ], +) +def test_ingredient_get_name_returns_name( + ingredient_type: str, name: str, price: float +): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_name() == name + + +@pytest.mark.parametrize( + "ingredient_type,name,price", + [ + (INGREDIENT_TYPE_SAUCE, "hot sauce", 10.0), + (INGREDIENT_TYPE_FILLING, "cutlet", 35.5), + ], +) +def test_ingredient_get_price_returns_price( + ingredient_type: str, name: str, price: float +): + ingredient = Ingredient(ingredient_type, name, price) + + assert ingredient.get_price() == price diff --git a/tests/test_praktikum.py b/tests/test_praktikum.py new file mode 100644 index 000000000..4a513bbaf --- /dev/null +++ b/tests/test_praktikum.py @@ -0,0 +1,84 @@ +import builtins +from unittest.mock import Mock, call + +import praktikum + + +def _prepare_main_dependencies(monkeypatch): + fake_buns = [Mock(name="bun_0"), Mock(name="bun_1")] + fake_ingredients = [Mock(name=f"ingredient_{index}") for index in range(6)] + fake_database = Mock( + available_buns=Mock(return_value=fake_buns), + available_ingredients=Mock(return_value=fake_ingredients), + ) + fake_burger = Mock() + fake_burger.get_receipt = Mock(return_value="test receipt") + + database_ctor = Mock(return_value=fake_database) + burger_ctor = Mock(return_value=fake_burger) + print_mock = Mock() + + monkeypatch.setattr(praktikum, "Database", database_ctor) + monkeypatch.setattr(praktikum, "Burger", burger_ctor) + monkeypatch.setattr(builtins, "print", print_mock) + + return { + "database_ctor": database_ctor, + "burger_ctor": burger_ctor, + "database": fake_database, + "burger": fake_burger, + "buns": fake_buns, + "ingredients": fake_ingredients, + "print_mock": print_mock, + } + + +def test_main_requests_buns_and_ingredients_from_database(monkeypatch): + deps = _prepare_main_dependencies(monkeypatch) + + praktikum.main() + + deps["database_ctor"].assert_called_once_with() + deps["database"].available_buns.assert_called_once_with() + deps["database"].available_ingredients.assert_called_once_with() + + +def test_main_sets_first_bun_from_database(monkeypatch): + deps = _prepare_main_dependencies(monkeypatch) + + praktikum.main() + + deps["burger"].set_buns.assert_called_once_with(deps["buns"][0]) + + +def test_main_adds_expected_ingredients(monkeypatch): + deps = _prepare_main_dependencies(monkeypatch) + + praktikum.main() + + deps["burger"].add_ingredient.assert_has_calls( + [ + call(deps["ingredients"][1]), + call(deps["ingredients"][4]), + call(deps["ingredients"][3]), + call(deps["ingredients"][5]), + ] + ) + + +def test_main_moves_and_removes_ingredient(monkeypatch): + deps = _prepare_main_dependencies(monkeypatch) + + praktikum.main() + + deps["burger"].move_ingredient.assert_called_once_with(2, 1) + deps["burger"].remove_ingredient.assert_called_once_with(3) + + +def test_main_prints_burger_receipt(monkeypatch): + deps = _prepare_main_dependencies(monkeypatch) + + praktikum.main() + + deps["burger"].get_receipt.assert_called_once_with() + deps["print_mock"].assert_called_once_with("test receipt")