added basic tests

This commit is contained in:
2024-02-22 13:17:43 +01:00
parent ac42045063
commit 20423b5655
3 changed files with 182 additions and 0 deletions

29
src/ex2_dictionary.py Normal file
View File

@ -0,0 +1,29 @@
# DO NOT CHANGE THE IMPORTS!
def calculate_price(articles: dict[str, int], cart: dict[str, int]) -> float:
price = 0
for name, amount in cart.items():
if name in articles:
price += articles[name] * amount
return price / 100
def by_amount(articles: dict[str, int], cart: dict[str, int]) -> dict[int, list[str]]:
out: dict[int, list[str]] = dict()
for name, amount in cart.items():
if name not in articles:
continue
if amount not in out:
out[amount] = [name]
else:
out[amount] += [name]
return out
if __name__ == "__main__":
articles_dict = {"apples": 100, "oranges": 100, "lemons": 200, "avocado": 500}
cart_dict = {"apples": 2, "oranges": 2, "lemons": 1, "bananas": 5}
assert calculate_price(articles_dict, cart_dict) == 6.0
assert by_amount(articles_dict, cart_dict) == {2: ['apples', 'oranges'], 1: ['lemons']}

114
src/test_dictionary.py Normal file
View File

@ -0,0 +1,114 @@
from ex2_dictionary import calculate_price
from util import eidp_test, has_annotations, imported_modules_of, has_no_annotations
import pytest
from enum import Enum
from copy import deepcopy
class Task(Enum):
MODULES, A, B = 'modules', 'calculate_price', 'by_amount'
EPS = 0.001
@pytest.mark.dependency(name=Task.MODULES)
@pytest.mark.timeout(10)
@eidp_test("unerlaubte Module importiert", -10)
def test_imported_modules():
import ex2_dictionary
assert len(list(imported_modules_of(ex2_dictionary))) == 0
@pytest.mark.dependency(depends=[Task.MODULES])
@pytest.mark.dependency(name=Task.A)
@pytest.mark.timeout(10)
@eidp_test("`calculate_price` nicht implementiert", -10)
def test_calculate_price_implemented():
from ex2_dictionary import calculate_price as _
@pytest.mark.dependency(depends=[Task.MODULES])
@pytest.mark.dependency(name=Task.B)
@pytest.mark.timeout(10)
@eidp_test("`by_amount` nicht implementiert", -10)
def test_by_amount_implemented():
from ex2_dictionary import by_amount as _
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("`calculate_price` hat keine Typannotationen", -1)
def test_no_typeannotation_calculate_price():
from ex2_dictionary import calculate_price
assert not has_no_annotations(calculate_price)
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("`calculate_price` hat inkorrekte Typannotationen", -0.5)
def test_typeannotation_calculate_price():
from ex2_dictionary import calculate_price
assert has_annotations(
calculate_price, [dict[str, int], dict[str, int]], float)
@pytest.mark.dependency(depends=[Task.B])
@pytest.mark.timeout(10)
@eidp_test("`by_amount` hat keine Typannotationen", -1)
def test_no_typeannotation_by_amount():
from ex2_dictionary import by_amount
assert not has_no_annotations(by_amount)
@pytest.mark.dependency(depends=[Task.B])
@pytest.mark.timeout(10)
@eidp_test("`by_amount` hat inkorrekte Typannotationen", -0.5)
def test_typeannotation_by_amount():
from ex2_dictionary import by_amount
assert has_annotations(
by_amount, [dict[str, int], dict[str, int]], dict[int, list[str]])
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("es wird nicht geschaut ob ein ein Produkt in `articles` ist", -2)
def test_article_not_in_articles():
articles = {'Coke': 200}
basket = {'Vitamine R': 1}
assert abs(calculate_price(articles, basket)) < EPS
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("Preis wird nicht in Euro (float) ausgegeben", -2)
def test_price_in_euro():
articles = {'Coke': 200}
basket = {'Vitamine R': 1, 'Coke': 2}
assert abs(calculate_price(articles, basket) - 4) < EPS
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("Preis wird nicht korrekt berechnet", -2)
def test_price_calc():
articles = {'Coke': 200, 'Eis': 350}
basket = {'Coke': 0}
assert abs(calculate_price(articles, basket)) < EPS
articles = {'Coke': 200, 'Eis': 350}
basket = {'Coke': 2, 'Eis': 3}
assert abs(calculate_price(articles, basket) - 14.5) < EPS
@pytest.mark.dependency(depends=[Task.A])
@pytest.mark.timeout(10)
@eidp_test("`articles`/`cart` wird veraendert", -2)
def test_sideeffectss():
a = {'Coke': 200, 'Eis': 350}
b = {'Coke': 2, 'Eis': 3}
calculate_price(a2 := deepcopy(a), b2 := deepcopy(b))
assert a == a2 and b2 == b
if __name__ == '__main__':
pass

39
src/util.py Normal file
View File

@ -0,0 +1,39 @@
from typing import Callable, Any, TypeAliasType, Iterator
from types import ModuleType
import inspect
def has_annotations(f: Callable, param_types: list[object], return_type: object = None) -> bool:
a = f.__annotations__
ret = a['return'] if 'return' in a else None
params = list(a.values())[:-1] if 'return' in a else list(a.values())
for param, param_type in zip(params, param_types):
if isinstance(param, TypeAliasType):
param = param.__value__
if isinstance(param_type, TypeAliasType):
param_type = param_type.__value__
if param != param_type:
return False
return ret == return_type
def has_no_annotations(f: Callable) -> bool:
return len(f.__annotations__) == 0
def imported_modules_of(module: ModuleType) -> Iterator[ModuleType]:
return map(lambda m: m[1], filter(lambda m: inspect.ismodule(m[1]), inspect.getmembers(module)))
def eidp_test(message: str, minus_points: float) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
def wrapper(test: Callable[..., Any]) -> Callable[..., Any]:
test.message = message
test.minus_points = minus_points
test.is_test = True
return test
return wrapper
def get_tests(module: ModuleType) -> dict[str, Callable[..., Any]]:
return dict([(name, f) for name, f in module.__dict__
if callable(f) and hasattr(f, "is_test")])