added basic tests
This commit is contained in:
29
src/ex2_dictionary.py
Normal file
29
src/ex2_dictionary.py
Normal 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
114
src/test_dictionary.py
Normal 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
39
src/util.py
Normal 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")])
|
Reference in New Issue
Block a user