Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

Testovaní softwaru

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.)

Rosemary's Grocery Store

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.

Zboží

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

  • název (name),
  • dobu trvanlivosti (days_left), tedy zbývající počet dní, do kdy je nutné zboží prodat, než se začne výrazně kazit, a
  • aktuální kvalitu (quality).

Speciální předměty

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ří

  • zrající sýr (“Aged Brie”),
  • diamant (“Diamond”) a
  • vstupenky (“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ží.

Aktualizace kvality

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.

Pravidla aktualizace

Funkce update() musí dodržet následující pravidla:

  • Název zboží nebo předmětu se nikdy nemění.
  • Kvalita zboží nikdy není menší než 0 a větší než 50 s jedinou výjimkou (Diamanty, viz níže).
  • Na konci každého dne, tj. při zavolání update(), se kvalita i trvanlivost zboží sníží o 1 (není-li uvedeno jinak).
  • Zboží s dosaženou dobou trvanlivosti (days_left == 0) ztrácí kvalitu 2x rychleji.
  • Diamanty neztrácejí ani kvalitu, ani trvanlivost. Kvalita diamantů je vždy 100 (výjimka z pravidla, že kvalita nemůže být vyšší než 50).
  • Zrající sýr s ubíhajícím časem kvalitu neztrácí, ale získává (kvalita se zvyšuje o 1), nezávisle na days_left.
  • Vstupenky s ubíhajícím časem také získávají na kvalitě:
    • +1, pokud do akce zbývá víc než 10 dní,
    • +2, pokud do akce zbývá 6-10 dní,
    • +3, pokud do akce zbývá 1-5 dní.
    • Ale v den konání akce se vstupenky stanou bezcennými (kvalita klesne na 0).

Váš úkol

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 sadu testů máte.

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é!

Obsah modulu s testy

Specifikace:

  • Všechny testy budou v jednom modulu (test_rosemary.py).
  • Modul na začátku importuje třídu 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!)
  • Není třeba používat jiné moduly kromě modulu rosemary.py. Moduly inspect a importlib budou zakázány.
  • Každý test bude samostatná funkce, jejíž jméno bude začínat na test_.
  • Každá taková funkce bude vracet buď
    • True, když se funkce update bude chovat podle testované specifikace, nebo
    • False, pokud detekujete odchylku od specifikací.
  • Testové funkce by měly být krátké! Funkce delší než 10 řádků budeme ignorovat!
  • Testové funkce nebudou přijímat žádné argumenty - náš odevzdavací skript jim žádné argumenty nepředá a bude vypisovat zvláštní chybu

Traceback (most recent call last):
  File "check_test.py", line 128, in run_test
    result = getattr(module, fn.fn_name)()

  • Testové funkce mohou běžet maximálně 1 vteřinu.
  • Doporučení: nazývejte testy popisnými jmény, aby bylo zřejmé, co testují. I přesto se jména pokuste udržet krátká!

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 budeme vaše testy testovat?

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

  • žádný test nehlásil chybu (nevracel False) pro správnou implementaci a zároveň
  • pro každou chybnou implementaci alespoň jeden z testů zahlásil chybu (vrátil False).
courses/b4b33rph/cviceni/program_po_tydnech/tyden_07_testovani.txt · Last modified: 2018/11/15 10:39 by jasekota