Cílem cvičení je procvičit si na příkladu, jak přetavit specifikace ve formě textového popisu do testů, které ověří shodu implementace se specifikacemi. (Jde především o princip vytváření testů; zde použitá realizace testů je zvolena z důvodu jednoduchosti.)
Zpětná vazba: (dle informací na cvičení) https://forms.gle/UpdGQy7WuzSuPXvk9
Dostali jste se do týmu, který vyvíjí počítačovou hru. Rosemary je obchodnice, která v této hře vede obchůdek se smíšeným zbožím (jehož implementaci máte ve správě). Obchod prodává především potraviny (tzv. normal items), ale Rosemary umí sehnat a nabídnout také jisté speciální předměty. Rosemary je pečlivá a vede si evidenci o stavu zboží, které má na skladě. Na konci každého dne stav všeho zboží aktualizuje.
Každé zboží nebo předmět, které má Rosemary na skladě, budeme reprezentovat instancí třídy Item
:
class Item: def __init__(self, name, days_left, quality): self.name = name self.days_left = days_left self.quality = quality
Každé zboží či předmět má tedy
name
, řetězec), days_left
, celé číslo), tedy zbývající počet dní (následujících po aktuálním dni), do kdy je nutné zboží prodat, než se začne výrazně kazit, aquality
, celé číslo).Rosemary může mít na skladě i speciální předměty, které se určují podle specifického jména. Mezi tyto speciální předměty patří
“Aged Brie”
),
“Diamond”
) a
“Tickets”
) na nějaké hodně žádané představení.
Tyto předměty jsou zvláštní v tom, že se chovají (stárnou) jinak než normální zboží.
Ve hře se na konci každého dne zavolá pro každé zboží na skladě funkce update()
. Ta aktualizuje (obvykle sníží) hodnoty days_left
a quality
:
>>> bread = Item('Bread', days_left=3, quality=5) >>> update(bread) >>> print(bread.days_left) 2 >>> print(bread.quality) 4
Poznámka 1: Funkce update()
je modifikátorem, tj. nic nevrací, ale přímo na místě modifikuje instanci třídy Item
, kterou jí předáme jako argument.
Poznámka 2: Z hlediska objektového návrhu by bylo lepší, kdyby update()
byla metodou ve třídě Item
a kdybychom mohli od třídy Item
odvodit další třídy, např. BrieItem, DiamondItem, apod., které by věděly, jak sami sebe aktualizovat. Celý zbytek hry ale využívá pouze Item
ve formě uvedené výše; zde popsané změny by si vyžádaly dalekosáhlé úpravy v kódu celé hry, a proto je zatím nemáme právo udělat.
Funkce update()
předpokládá, že jako vstup obdrží instanci třídy Item
naplněnou platnými hodnotami, a musí dodržet následující pravidla:
update()
, se kvalita i trvanlivost zboží sníží o 1 (není-li uvedeno jinak).
days_left <= 0
) ztrácí kvalitu 2x rychleji.
days_left
.
Funkce update()
už je implementována. Vaším úkolem je sestavit sadů testů pro tuto funkci, které ověří, že se implementace funkce chová podle specifikací. Konkrétně je vaším úkolem vytvořit modul test_rosemary.py
s co nejúplnější sadou testů, který nahrajete do BRUTE, kde vám oznámíme, jak dobrou/úplnou sadu testů máte.
Testujte jen výše uvedené specifikace. Funkce update()
předpokládá, že bude vždy volána s 1 argumentem, kterým bude instance třídy Item
, a tato instance bude obsahovat platná data. Není úkolem funkce update()
testovat chybný vstup, a proto ani vy byste neměli testovat, jak se funkce zachová při chybném vstupu.
Při testování softwaru je ideální v každém testu testovat pouze jedinou věc tak, aby při selhání testu bylo ihned zřejmé, co je špatně. (Kdyby měl test mnoho možností, proč selhat, netušili byste, která z nich za selhání může.) Je proto zcela obvyklé mít hodně testů a mít je krátké!
Specifikace:
test_rosemary.py
).
Item
a funkci update
z modulu rosemary.py
. Tento modul nemáte k dispozici, v BRUTE jej připojíme k vašemu odevzdanému modulu s testy. (Nic vám nebrání zkusit si současně s psaním testů vytvořit vlastní implementaci funkce update
v modulu rosemary.py
! Tento modul ale neodevzdávejte!)
rosemary.py
. Moduly inspect
a importlib
budou zakázány.
test_
.
True
, když se funkce update
bude chovat podle testované specifikace, nebo
False
, pokud detekujete odchylku od specifikací.
Traceback (most recent call last): File "check_test.py", line 128, in run_test result = getattr(module, fn.fn_name)()
Vzor souboru test_rosemary.py
:
from rosemary import Item, update def test_normal_item_decreases_days_left(): # Prepare for the test item = Item('Bread', days_left=3, quality=5) # Call to the tested function update(item) # Check the specification return item.days_left == 2 def test_... ... def test_... ...
Poznámka: Zde se zcela obejdeme bez jakéhokoli testovacího frameworku, dokonce i bez modulu testing.py
, který jsme si vytvářeli na přednášce.
Jak už bylo řečeno, v BRUTE k vašemu testovému modulu test_rosemary.py
připojíme naši implementaci funkce update()
v modulu rosemary.py
. Máme k dispozici nejen (snad) správnou implementaci, ale i mnoho chybných s mnoha různými chybami. Vaši sadu testů vyzkoušíme na všech implementacích funkce update()
. Cílem je, aby
False
) pro správnou implementaci a zároveň
False
).
Zůstává příprava z minula!