From 92a53cdab79edc2100a717f947c27fc7c8ff74fb Mon Sep 17 00:00:00 2001 From: Henry Nomichith Date: Thu, 29 Aug 2024 12:42:35 +0200 Subject: [PATCH] chore(supermarket): add tests --- src/supermarket_receipt/.gitignore | 78 ------------------- src/supermarket_receipt/README.md | 33 +++----- src/supermarket_receipt/python/.gitignore | 78 ------------------- src/supermarket_receipt/python/README.md | 26 ------- src/supermarket_receipt/python/catalog.py | 9 --- .../python/model_objects.py | 38 --------- .../python/requirements.txt | 4 - .../python/tests/test_supermarket.py | 32 -------- src/supermarket_receipt/receipt.py | 35 --------- src/supermarket_receipt/receipt_printer.py | 56 ------------- src/supermarket_receipt/requirements.txt | 8 +- src/supermarket_receipt/shopping_cart.py | 68 ---------------- src/{ => supermarket_receipt/src}/__init__.py | 0 src/supermarket_receipt/{ => src}/catalog.py | 0 .../{ => src}/model_objects.py | 0 .../{python => src}/receipt.py | 0 .../{python => src}/shopping_cart.py | 2 +- .../{python => src}/teller.py | 7 +- src/supermarket_receipt/teller.py | 26 ------- .../{python => }/tests/__init__.py | 0 .../tests/approvaltests_config.json | 2 +- ...supermarket.test_empty_basket.approved.txt | 2 + ...test_five_for_amount_discount.approved.txt | 5 ++ ...mount_discount_bought_too_few.approved.txt | 4 + ..._supermarket.test_no_discount.approved.txt | 4 + ...ket.test_ten_percent_discount.approved.txt | 5 ++ ...t.test_three_for_two_discount.approved.txt | 5 ++ ...hree_for_two_discount_too_few.approved.txt | 4 + ....test_two_for_amount_discount.approved.txt | 5 ++ ...mount_discount_bought_too_few.approved.txt | 3 + src/supermarket_receipt/tests/conftest.py | 34 ++++++++ .../{python => }/tests/fake_catalog.py | 2 +- .../{python => tests}/receipt_printer.py | 2 +- .../tests/test_supermarket.py | 78 +++++++++++++++++++ 34 files changed, 173 insertions(+), 482 deletions(-) delete mode 100644 src/supermarket_receipt/.gitignore delete mode 100644 src/supermarket_receipt/python/.gitignore delete mode 100644 src/supermarket_receipt/python/README.md delete mode 100644 src/supermarket_receipt/python/catalog.py delete mode 100644 src/supermarket_receipt/python/model_objects.py delete mode 100644 src/supermarket_receipt/python/requirements.txt delete mode 100644 src/supermarket_receipt/python/tests/test_supermarket.py delete mode 100644 src/supermarket_receipt/receipt.py delete mode 100644 src/supermarket_receipt/receipt_printer.py delete mode 100644 src/supermarket_receipt/shopping_cart.py rename src/{ => supermarket_receipt/src}/__init__.py (100%) rename src/supermarket_receipt/{ => src}/catalog.py (100%) rename src/supermarket_receipt/{ => src}/model_objects.py (100%) rename src/supermarket_receipt/{python => src}/receipt.py (100%) rename src/supermarket_receipt/{python => src}/shopping_cart.py (97%) rename src/supermarket_receipt/{python => src}/teller.py (81%) delete mode 100644 src/supermarket_receipt/teller.py rename src/supermarket_receipt/{python => }/tests/__init__.py (100%) rename src/supermarket_receipt/{python => }/tests/approvaltests_config.json (94%) create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_empty_basket.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount_bought_too_few.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_no_discount.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_ten_percent_discount.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount_too_few.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount.approved.txt create mode 100644 src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount_bought_too_few.approved.txt create mode 100644 src/supermarket_receipt/tests/conftest.py rename src/supermarket_receipt/{python => }/tests/fake_catalog.py (88%) rename src/supermarket_receipt/{python => tests}/receipt_printer.py (97%) create mode 100644 src/supermarket_receipt/tests/test_supermarket.py diff --git a/src/supermarket_receipt/.gitignore b/src/supermarket_receipt/.gitignore deleted file mode 100644 index e48c9ad..0000000 --- a/src/supermarket_receipt/.gitignore +++ /dev/null @@ -1,78 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ -.pytest_cache - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask instance folder -instance/ - -# Sphinx documentation -docs/_build/ - -# MkDocs documentation -/site/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version -venv -.venv - -.idea -.vscode diff --git a/src/supermarket_receipt/README.md b/src/supermarket_receipt/README.md index 30f7d56..92fe52d 100644 --- a/src/supermarket_receipt/README.md +++ b/src/supermarket_receipt/README.md @@ -1,26 +1,15 @@ -# Supermarket Receipt in [Python](https://www.python.org/) +# Supermarket Receipt -## Setup - -* Have Python installed -* Clone the repository -* On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory -* On the command line, install requirements, e.g. on the`python -m pip install -r requirements.txt` - -## Running Tests - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run +You are working on the software for a supermarket, in particular for the teller machine that cashiers use to calculate the price of a shopping cart full of items and give the customer a receipt. The supermarket has a catalog of products for sale at various prices. Normally the price of a shopping cart is the sum of the prices of all the items in it. However, at any given time there might be special offers and price reductions on particular products. For example: -``` -pytest -``` +- 10% discount +- 3 for the price of 2 +- 2 items for a reduced price +- 5 items for a reduced price -## Optional: Running [TextTest](https://www.texttest.org/) Tests +The starting position for this exercise contains the code for setting up the Teller object, a catalog of products, the shopping cart, and any special offers. It can calculate the price of a shopping cart and generate a receipt, but so far there aren't many test cases. -Install TextTest according to the [instructions](https://www.texttest.org/index.html#getting-started-with-texttest) (platform specific). - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run - -``` -texttest -a sr -d . -``` +## Setup +- make a venv +- install requirements, e.g. `python -m pip install -r requirements.txt` +- use pytest to run the tests diff --git a/src/supermarket_receipt/python/.gitignore b/src/supermarket_receipt/python/.gitignore deleted file mode 100644 index e48c9ad..0000000 --- a/src/supermarket_receipt/python/.gitignore +++ /dev/null @@ -1,78 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover -.hypothesis/ -.pytest_cache - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask instance folder -instance/ - -# Sphinx documentation -docs/_build/ - -# MkDocs documentation -/site/ - -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints - -# pyenv -.python-version -venv -.venv - -.idea -.vscode diff --git a/src/supermarket_receipt/python/README.md b/src/supermarket_receipt/python/README.md deleted file mode 100644 index 30f7d56..0000000 --- a/src/supermarket_receipt/python/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Supermarket Receipt in [Python](https://www.python.org/) - -## Setup - -* Have Python installed -* Clone the repository -* On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory -* On the command line, install requirements, e.g. on the`python -m pip install -r requirements.txt` - -## Running Tests - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run - -``` -pytest -``` - -## Optional: Running [TextTest](https://www.texttest.org/) Tests - -Install TextTest according to the [instructions](https://www.texttest.org/index.html#getting-started-with-texttest) (platform specific). - -On the command line, enter the `SupermarketReceipt-Refactoring-Kata/python` directory and run - -``` -texttest -a sr -d . -``` diff --git a/src/supermarket_receipt/python/catalog.py b/src/supermarket_receipt/python/catalog.py deleted file mode 100644 index e522c8b..0000000 --- a/src/supermarket_receipt/python/catalog.py +++ /dev/null @@ -1,9 +0,0 @@ - -class SupermarketCatalog: - - def add_product(self, product, price): - raise Exception("cannot be called from a unit test - it accesses the database") - - def unit_price(self, product): - raise Exception("cannot be called from a unit test - it accesses the database") - diff --git a/src/supermarket_receipt/python/model_objects.py b/src/supermarket_receipt/python/model_objects.py deleted file mode 100644 index 2d8d0ad..0000000 --- a/src/supermarket_receipt/python/model_objects.py +++ /dev/null @@ -1,38 +0,0 @@ -from enum import Enum - - -class Product: - def __init__(self, name, unit): - self.name = name - self.unit = unit - - -class ProductQuantity: - def __init__(self, product, quantity): - self.product = product - self.quantity = quantity - - -class ProductUnit(Enum): - EACH = 1 - KILO = 2 - - -class SpecialOfferType(Enum): - THREE_FOR_TWO = 1 - TEN_PERCENT_DISCOUNT = 2 - TWO_FOR_AMOUNT = 3 - FIVE_FOR_AMOUNT = 4 - -class Offer: - def __init__(self, offer_type, product, argument): - self.offer_type = offer_type - self.product = product - self.argument = argument - - -class Discount: - def __init__(self, product, description, discount_amount): - self.product = product - self.description = description - self.discount_amount = discount_amount diff --git a/src/supermarket_receipt/python/requirements.txt b/src/supermarket_receipt/python/requirements.txt deleted file mode 100644 index 20fb255..0000000 --- a/src/supermarket_receipt/python/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -approvaltests -python-dateutil -pytest-approvaltests - diff --git a/src/supermarket_receipt/python/tests/test_supermarket.py b/src/supermarket_receipt/python/tests/test_supermarket.py deleted file mode 100644 index 3fafd09..0000000 --- a/src/supermarket_receipt/python/tests/test_supermarket.py +++ /dev/null @@ -1,32 +0,0 @@ -import pytest - -from model_objects import Product, SpecialOfferType, ProductUnit -from shopping_cart import ShoppingCart -from teller import Teller -from tests.fake_catalog import FakeCatalog - - -def test_ten_percent_discount(): - catalog = FakeCatalog() - toothbrush = Product("toothbrush", ProductUnit.EACH) - catalog.add_product(toothbrush, 0.99) - - apples = Product("apples", ProductUnit.KILO) - catalog.add_product(apples, 1.99) - - teller = Teller(catalog) - teller.add_special_offer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0) - - cart = ShoppingCart() - cart.add_item_quantity(apples, 2.5) - - receipt = teller.checks_out_articles_from(cart) - - assert 4.975 == pytest.approx(receipt.total_price(), 0.01) - assert [] == receipt.discounts - assert 1 == len(receipt.items) - receipt_item = receipt.items[0] - assert apples == receipt_item.product - assert 1.99 == receipt_item.price - assert 2.5 * 1.99 == pytest.approx(receipt_item.total_price, 0.01) - assert 2.5 == receipt_item.quantity diff --git a/src/supermarket_receipt/receipt.py b/src/supermarket_receipt/receipt.py deleted file mode 100644 index 2a0a219..0000000 --- a/src/supermarket_receipt/receipt.py +++ /dev/null @@ -1,35 +0,0 @@ - -class ReceiptItem: - def __init__(self, product, quantity, price, total_price): - self.product = product - self.quantity = quantity - self.price = price - self.total_price = total_price - - -class Receipt: - def __init__(self): - self._items = [] - self._discounts = [] - - def total_price(self): - total = 0 - for item in self.items: - total += item.total_price - for discount in self.discounts: - total += discount.discount_amount - return total - - def add_product(self, product, quantity, price, total_price): - self._items.append(ReceiptItem(product, quantity, price, total_price)) - - def add_discount(self, discount): - self._discounts.append(discount) - - @property - def items(self): - return self._items[:] - - @property - def discounts(self): - return self._discounts[:] diff --git a/src/supermarket_receipt/receipt_printer.py b/src/supermarket_receipt/receipt_printer.py deleted file mode 100644 index 7550fe9..0000000 --- a/src/supermarket_receipt/receipt_printer.py +++ /dev/null @@ -1,56 +0,0 @@ -from model_objects import ProductUnit - -class ReceiptPrinter: - - def __init__(self, columns=40): - self.columns = columns - - def print_receipt(self, receipt): - result = "" - for item in receipt.items: - receipt_item = self.print_receipt_item(item) - result += receipt_item - - for discount in receipt.discounts: - discount_presentation = self.print_discount(discount) - result += discount_presentation - - result += "\n" - result += self.present_total(receipt) - return str(result) - - def print_receipt_item(self, item): - total_price_printed = self.print_price(item.total_price) - name = item.product.name - line = self.format_line_with_whitespace(name, total_price_printed) - if item.quantity != 1: - line += f" {self.print_price(item.price)} * {self.print_quantity(item)}\n" - return line - - def format_line_with_whitespace(self, name, value): - line = name - whitespace_size = self.columns - len(name) - len(value) - for i in range(whitespace_size): - line += " " - line += value - line += "\n" - return line - - def print_price(self, price): - return "%.2f" % price - - def print_quantity(self, item): - if ProductUnit.EACH == item.product.unit: - return str(item.quantity) - else: - return '%.3f' % item.quantity - - def print_discount(self, discount): - name = f"{discount.description} ({discount.product.name})" - value = self.print_price(discount.discount_amount) - return self.format_line_with_whitespace(name, value) - - def present_total(self, receipt): - name = "Total: " - value = self.print_price(receipt.total_price()) - return self.format_line_with_whitespace(name, value) diff --git a/src/supermarket_receipt/requirements.txt b/src/supermarket_receipt/requirements.txt index 20fb255..54011bf 100644 --- a/src/supermarket_receipt/requirements.txt +++ b/src/supermarket_receipt/requirements.txt @@ -1,4 +1,4 @@ -approvaltests -python-dateutil -pytest-approvaltests - +pytest==7.3.2 +approvaltests==10.0.0 +pytest-approvaltests==0.2.4 +coverage diff --git a/src/supermarket_receipt/shopping_cart.py b/src/supermarket_receipt/shopping_cart.py deleted file mode 100644 index 4e36cc5..0000000 --- a/src/supermarket_receipt/shopping_cart.py +++ /dev/null @@ -1,68 +0,0 @@ -import math - -from model_objects import ProductQuantity, SpecialOfferType, Discount - - -class ShoppingCart: - - def __init__(self): - self._items = [] - self._product_quantities = {} - - @property - def items(self): - return self._items - - def add_item(self, product): - self.add_item_quantity(product, 1.0) - - @property - def product_quantities(self): - return self._product_quantities - - def add_item_quantity(self, product, quantity): - self._items.append(ProductQuantity(product, quantity)) - if product in self._product_quantities.keys(): - self._product_quantities[product] = self._product_quantities[product] + quantity - else: - self._product_quantities[product] = quantity - - def handle_offers(self, receipt, offers, catalog): - for p in self._product_quantities.keys(): - quantity = self._product_quantities[p] - if p in offers.keys(): - offer = offers[p] - unit_price = catalog.unit_price(p) - quantity_as_int = int(quantity) - discount = None - x = 1 - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO: - x = 3 - - elif offer.offer_type == SpecialOfferType.TWO_FOR_AMOUNT: - x = 2 - if quantity_as_int >= 2: - total = offer.argument * (quantity_as_int / x) + quantity_as_int % 2 * unit_price - discount_n = unit_price * quantity - total - discount = Discount(p, "2 for " + str(offer.argument), -discount_n) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT: - x = 5 - - number_of_x = math.floor(quantity_as_int / x) - if offer.offer_type == SpecialOfferType.THREE_FOR_TWO and quantity_as_int > 2: - discount_amount = quantity * unit_price - ( - (number_of_x * 2 * unit_price) + quantity_as_int % 3 * unit_price) - discount = Discount(p, "3 for 2", -discount_amount) - - if offer.offer_type == SpecialOfferType.TEN_PERCENT_DISCOUNT: - discount = Discount(p, str(offer.argument) + "% off", - -quantity * unit_price * offer.argument / 100.0) - - if offer.offer_type == SpecialOfferType.FIVE_FOR_AMOUNT and quantity_as_int >= 5: - discount_total = unit_price * quantity - ( - offer.argument * number_of_x + quantity_as_int % 5 * unit_price) - discount = Discount(p, str(x) + " for " + str(offer.argument), -discount_total) - - if discount: - receipt.add_discount(discount) diff --git a/src/__init__.py b/src/supermarket_receipt/src/__init__.py similarity index 100% rename from src/__init__.py rename to src/supermarket_receipt/src/__init__.py diff --git a/src/supermarket_receipt/catalog.py b/src/supermarket_receipt/src/catalog.py similarity index 100% rename from src/supermarket_receipt/catalog.py rename to src/supermarket_receipt/src/catalog.py diff --git a/src/supermarket_receipt/model_objects.py b/src/supermarket_receipt/src/model_objects.py similarity index 100% rename from src/supermarket_receipt/model_objects.py rename to src/supermarket_receipt/src/model_objects.py diff --git a/src/supermarket_receipt/python/receipt.py b/src/supermarket_receipt/src/receipt.py similarity index 100% rename from src/supermarket_receipt/python/receipt.py rename to src/supermarket_receipt/src/receipt.py diff --git a/src/supermarket_receipt/python/shopping_cart.py b/src/supermarket_receipt/src/shopping_cart.py similarity index 97% rename from src/supermarket_receipt/python/shopping_cart.py rename to src/supermarket_receipt/src/shopping_cart.py index 4e36cc5..41eeb63 100644 --- a/src/supermarket_receipt/python/shopping_cart.py +++ b/src/supermarket_receipt/src/shopping_cart.py @@ -1,6 +1,6 @@ import math -from model_objects import ProductQuantity, SpecialOfferType, Discount +from src.model_objects import ProductQuantity, SpecialOfferType, Discount class ShoppingCart: diff --git a/src/supermarket_receipt/python/teller.py b/src/supermarket_receipt/src/teller.py similarity index 81% rename from src/supermarket_receipt/python/teller.py rename to src/supermarket_receipt/src/teller.py index 8bfdbe8..ccafdf7 100644 --- a/src/supermarket_receipt/python/teller.py +++ b/src/supermarket_receipt/src/teller.py @@ -1,5 +1,5 @@ -from model_objects import Offer -from receipt import Receipt +from src.model_objects import Offer +from src.receipt import Receipt class Teller: @@ -24,3 +24,6 @@ def checks_out_articles_from(self, the_cart): the_cart.handle_offers(receipt, self.offers, self.catalog) return receipt + + def product_with_name(self, name): + return self.catalog.products.get(name, None) \ No newline at end of file diff --git a/src/supermarket_receipt/teller.py b/src/supermarket_receipt/teller.py deleted file mode 100644 index 8bfdbe8..0000000 --- a/src/supermarket_receipt/teller.py +++ /dev/null @@ -1,26 +0,0 @@ -from model_objects import Offer -from receipt import Receipt - - -class Teller: - - def __init__(self, catalog): - self.catalog = catalog - self.offers = {} - - def add_special_offer(self, offer_type, product, argument): - self.offers[product] = Offer(offer_type, product, argument) - - def checks_out_articles_from(self, the_cart): - receipt = Receipt() - product_quantities = the_cart.items - for pq in product_quantities: - p = pq.product - quantity = pq.quantity - unit_price = self.catalog.unit_price(p) - price = quantity * unit_price - receipt.add_product(p, quantity, unit_price, price) - - the_cart.handle_offers(receipt, self.offers, self.catalog) - - return receipt diff --git a/src/supermarket_receipt/python/tests/__init__.py b/src/supermarket_receipt/tests/__init__.py similarity index 100% rename from src/supermarket_receipt/python/tests/__init__.py rename to src/supermarket_receipt/tests/__init__.py diff --git a/src/supermarket_receipt/python/tests/approvaltests_config.json b/src/supermarket_receipt/tests/approvaltests_config.json similarity index 94% rename from src/supermarket_receipt/python/tests/approvaltests_config.json rename to src/supermarket_receipt/tests/approvaltests_config.json index 550e664..1ceea7e 100644 --- a/src/supermarket_receipt/python/tests/approvaltests_config.json +++ b/src/supermarket_receipt/tests/approvaltests_config.json @@ -1,3 +1,3 @@ { "subdirectory": "approved_files" -} +} \ No newline at end of file diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_empty_basket.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_empty_basket.approved.txt new file mode 100644 index 0000000..b748104 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_empty_basket.approved.txt @@ -0,0 +1,2 @@ + +Total: 0.00 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount.approved.txt new file mode 100644 index 0000000..0672d65 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount.approved.txt @@ -0,0 +1,5 @@ +toothbrush 4.95 + 0.99 * 5 +5 for 4.0 (toothbrush) -0.95 + +Total: 4.00 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount_bought_too_few.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount_bought_too_few.approved.txt new file mode 100644 index 0000000..3eb259e --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_five_for_amount_discount_bought_too_few.approved.txt @@ -0,0 +1,4 @@ +toothbrush 3.96 + 0.99 * 4 + +Total: 3.96 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_no_discount.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_no_discount.approved.txt new file mode 100644 index 0000000..df0a8c5 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_no_discount.approved.txt @@ -0,0 +1,4 @@ +apples 4.97 + 1.99 * 2.500 + +Total: 4.97 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_ten_percent_discount.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_ten_percent_discount.approved.txt new file mode 100644 index 0000000..36c4f9e --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_ten_percent_discount.approved.txt @@ -0,0 +1,5 @@ +toothbrush 1.98 + 0.99 * 2 +10.0% off (toothbrush) -0.20 + +Total: 1.78 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount.approved.txt new file mode 100644 index 0000000..532e23a --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount.approved.txt @@ -0,0 +1,5 @@ +toothbrush 2.97 + 0.99 * 3 +3 for 2 (toothbrush) -0.99 + +Total: 1.98 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount_too_few.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount_too_few.approved.txt new file mode 100644 index 0000000..aebb767 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_three_for_two_discount_too_few.approved.txt @@ -0,0 +1,4 @@ +toothbrush 1.98 + 0.99 * 2 + +Total: 1.98 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount.approved.txt new file mode 100644 index 0000000..e41e488 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount.approved.txt @@ -0,0 +1,5 @@ +toothbrush 4.95 + 0.99 * 5 +2 for 1.8 (toothbrush) 0.54 + +Total: 5.49 diff --git a/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount_bought_too_few.approved.txt b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount_bought_too_few.approved.txt new file mode 100644 index 0000000..3260821 --- /dev/null +++ b/src/supermarket_receipt/tests/approved_files/test_supermarket.test_two_for_amount_discount_bought_too_few.approved.txt @@ -0,0 +1,3 @@ +toothbrush 0.99 + +Total: 0.99 diff --git a/src/supermarket_receipt/tests/conftest.py b/src/supermarket_receipt/tests/conftest.py new file mode 100644 index 0000000..a77c4c9 --- /dev/null +++ b/src/supermarket_receipt/tests/conftest.py @@ -0,0 +1,34 @@ +import pytest + +from tests.fake_catalog import FakeCatalog +from src.model_objects import Product, ProductUnit +from src.shopping_cart import ShoppingCart +from src.teller import Teller + + +@pytest.fixture +def toothbrush(): + return Product("toothbrush", ProductUnit.EACH) + + +@pytest.fixture +def apples(): + return Product("apples", ProductUnit.KILO) + + +@pytest.fixture +def catalog(toothbrush, apples): + catalog = FakeCatalog() + catalog.add_product(toothbrush, 0.99) + catalog.add_product(apples, 1.99) + return catalog + + +@pytest.fixture +def teller(catalog): + return Teller(catalog) + + +@pytest.fixture +def cart(): + return ShoppingCart() diff --git a/src/supermarket_receipt/python/tests/fake_catalog.py b/src/supermarket_receipt/tests/fake_catalog.py similarity index 88% rename from src/supermarket_receipt/python/tests/fake_catalog.py rename to src/supermarket_receipt/tests/fake_catalog.py index 86e5811..65a143d 100644 --- a/src/supermarket_receipt/python/tests/fake_catalog.py +++ b/src/supermarket_receipt/tests/fake_catalog.py @@ -1,4 +1,4 @@ -from catalog import SupermarketCatalog +from src.catalog import SupermarketCatalog class FakeCatalog(SupermarketCatalog): diff --git a/src/supermarket_receipt/python/receipt_printer.py b/src/supermarket_receipt/tests/receipt_printer.py similarity index 97% rename from src/supermarket_receipt/python/receipt_printer.py rename to src/supermarket_receipt/tests/receipt_printer.py index 7550fe9..6f5d663 100644 --- a/src/supermarket_receipt/python/receipt_printer.py +++ b/src/supermarket_receipt/tests/receipt_printer.py @@ -1,4 +1,4 @@ -from model_objects import ProductUnit +from src.model_objects import ProductUnit class ReceiptPrinter: diff --git a/src/supermarket_receipt/tests/test_supermarket.py b/src/supermarket_receipt/tests/test_supermarket.py new file mode 100644 index 0000000..a04845a --- /dev/null +++ b/src/supermarket_receipt/tests/test_supermarket.py @@ -0,0 +1,78 @@ +import approvaltests +import pytest + +from src.model_objects import SpecialOfferType +from tests.receipt_printer import ReceiptPrinter + + +def test_empty_basket(teller, cart, toothbrush, apples): + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + +def test_no_discount(teller, cart, toothbrush, apples): + teller.add_special_offer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0) + cart.add_item_quantity(apples, 2.5) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + + +def test_ten_percent_discount(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.TEN_PERCENT_DISCOUNT, toothbrush, 10.0) + cart.add_item_quantity(toothbrush, 2) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + + +def test_three_for_two_discount(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.THREE_FOR_TWO, toothbrush, 10.0) + cart.add_item_quantity(toothbrush, 3) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + +def test_three_for_two_discount_too_few(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.THREE_FOR_TWO, toothbrush, 10.0) + cart.add_item_quantity(toothbrush, 2) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + + +def test_five_for_amount_discount(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.FIVE_FOR_AMOUNT, toothbrush, 4.0) + cart.add_item_quantity(toothbrush, 5) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + +def test_five_for_amount_discount_bought_too_few(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.FIVE_FOR_AMOUNT, toothbrush, 4.0) + cart.add_item_quantity(toothbrush, 4) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + +def test_two_for_amount_discount(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.TWO_FOR_AMOUNT, toothbrush, 1.80) + cart.add_item_quantity(toothbrush, 5) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt)) + +def test_two_for_amount_discount_bought_too_few(teller, cart, toothbrush): + teller.add_special_offer(SpecialOfferType.TWO_FOR_AMOUNT, toothbrush, 1.80) + cart.add_item_quantity(toothbrush, 1) + + receipt = teller.checks_out_articles_from(cart) + + approvaltests.verify(ReceiptPrinter().print_receipt(receipt))