Programátorská práce nespočívá jen v tom, program napsat. Důležité je si i ověřit, že opravdu funguje (a případně ho pak opravit). Ověřování, že program funguje, se říká testování.
Zatím jsi asi svoje programy testovala tak, že jsi je zkusila spustit, něco zadala a podívala se, jestli jsou výsledky v pořádku. U větších programů, které budou mít více a více možností, ale bude těžší a těžší takhle zkontrolovat, jestli všechny ty možnosti fungují, jak mají.
Proto si programátoři, místo aby program zkoušeli ručně, píšou jiné programy, které testují jejich výtvory za ně.
Automatické testy jsou funkce, které zkontrolují, že náš program funguje správně. Spuštěním testů můžeš kdykoli ověřit, že kód funguje. Hlavní výhoda je, že když v otestovaném kódu v budoucnu uděláš nějakou změnu, testy ověří, že jsi nerozbila nic, co dříve fungovalo.
Zatím jsme v kurzu pracovaly s tím, co se instaluje
se samotným Pythonem – s moduly jako math
a turtle
.
Kromě takových modulů ale existuje ale velká spousta
dalších knihoven, které nejsou přímo v Pythonu, ale dají se doinstalovat
a používat.
Na testy je v samotném Pythonu zabudovaná knihovna unittest
.
Ta je ale celkem složitá na použití, proto ji my používat nebudeme.
Nainstalujeme si knihovnu pytest
, která se používá
mnohem jednodušeji a je velice populární.
Knihovny se instalují do aktivního virtuálního prostředí. Jak se dělá a spouští virtuální prostředí ses naučila při instalaci Pythonu, ale teprve teď to začíná být opravdu důležité. Ujisti se, že máš virtuální prostředí aktivované.
Potom zadej následující příkaz.
(Je to příkaz příkazové řádky, podobně jako
cd
nebo mkdir
; nezadávej ho do Pythonu.)
(venv)$ python -m pip install pytest
Co to znamená?
python -m pip
zavolá Python s tím, že má pustit modul
pip
. Tento modul umí instalovat nebo
odinstalovávat knihovny.
(Jestli si pamatuješ vytváření virtuálního prostředí, použila jsi tam
příkaz python -m venv
– modul venv
umí vytvářet virtuální prostředí.)
No a slova install pytest
říkají Pipu, že má nainstalovat pytest
.
Nápověda k použití Pipu se dá vypsat pomocí příkazu
python -m pip --help
.
Pro Windows
Jsi-li na Windows, od této lekce začne být důležité
spouštět pythonní programy pomocí python program.py
, ne jen
program.py
.
Ačkoli se v těchto materiálech všude používá python
na začátku, zatím
mohlo všechno fungovat i bez toho.
Program se ale bez příkazu python
může spustit v jiném Pythonu,
než v tom z virtuálního prostředí – a tam pytest
nebude k dispozici.
Nejdříve si testování ukážeme na jednoduchém příkladu.
Tady je funkce secti
, která umí sečíst
dvě čísla, a další funkce, která testuje, jestli se
secti
pro určité hodnoty
chová správně.
Kód si opiš do souboru test_secteni.py
,
v novém prázdném adresáři.
Pro pytest
je (ve výchozím nastavení)
důležité, aby jména jak souborů s testy, tak
samotných testovacích funkcí, začínala na
test_
.
def secti(a, b):
return a + b
def test_secti():
assert secti(1, 2) == 3
Co se v té testovací funkci děje?
Příkaz assert
vyhodnotí výraz za ním
a pokud výsledek není pravdivý, vyvolá výjimku,
která způsobí, že test selže.
Můžeš si představit, že assert a == b
dělá následující:
if not (a == b):
raise AssertionError('Test selhal!')
Zatím assert
nepoužívej jinde než v testovacích funkcích.
V „normálním” kódu má assert
vlastnosti,
do kterých teď nebudeme zabředávat.
Testy se spouští zadáním příkazu
python -m pytest -v
následovaným názvem souboru s testy.
Tedy v překladu: Pythone, pusť
modul pytest,
v „ukecaném” režimu (angl. verbose) nad zadaným souborem.
$ python -m pytest -v test_secteni.py
============= test session starts =============
platform linux -- Python 3.6.0, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- env/bin/python
cachedir: .cache
rootdir: naucse, inifile:
collecting ... collected 1 items
test_secteni.py::test_secti PASSED
============= 1 passed in 0.00 seconds =============
Tento příkaz projde zadaný soubor, zavolá v něm všechny funkce,
jejichž jméno začíná na test_
, a ověří, že nevyvolají žádnou
výjimku – typicky výjimku z příkazu assert
.
Pokud výjimka nastane, dá to pytest
velice červeně
najevo a přidá několik informací, které můžou
usnadnit nalezení a opravu chyby.
Argument s názvem souboru můžeme vynechat: python -m pytest -v
V takovém případě pytest
projde aktuální adresář a spustí testy
ze všech souborů, jejichž jméno začíná na test_
. Místo souboru
lze též uvést adresář a pytest
vyhledá testy v něm.
Zkus si změnit funkci secti
(nebo její test) a podívat se,
jak to vypadá když test „neprojde“.
Testy se většinou nepíšou přímo ke kódu, ale do souboru vedle. Je to tak přehlednější a taky to pak zjednodušuje distribuci – předávání kódu někomu, kdo ho chce jen spustit a testy nepotřebuje.
Rozděl soubor s testem sečítání: funkci secti
přesuň do modulu secteni.py
,
a v test_secteni.py
nech jenom test.
Do test_secteni.py
pak na začátek přidej from secteni import secti
,
aby byla funkce testu k dispozici.
Test by měl opět projít.
Automatické testy musí projít „bez dozoru“. V praxi se často automaticky spouští, případné chyby se automaticky oznamují (např. e-mailem) a fungující kód se automaticky začne používat dál (nebo se rovnou vydá zákazníkům).
Co to znamená pro nás?
Funkce input
v testech nefunguje. Nemá koho by se zeptala; „za klávesnicí“
nemusí nikdo sedět.
To může někdy „ztěžovat práci“. Ukážeme si to na složitějším projektu: na 1D piškvorkách.
Nemáš-li hotové 1D piškvorky, následující sekce budou jen teorietické. Učíš-li se z domu, dodělej si Piškvorky než budeš pokračovat dál! Zadání najdeš (prozatím) v projektech pro PyLadies na straně 2.
Kód pro 1D Piškvorky může rámcově vypadat zhruba takto:
import random # (příp. import jiných věci, které budou potřeba)
def tah(pole, cislo_policka, symbol):
"""Vrátí pole s daným symbolem umístěným na danou pozici"""
...
def tah_hrace(pole):
"""Zeptá se hráče kam chce hrát a vrátí pole se zaznamenaným tahem"""
...
input('Kam chceš hrát? ')
...
def piskvorky1d():
"""Spustí hru
Vytvoří hrací pole a střídavě volá tah_hrace a tah_pocitace
dokud někdo nevyhraje"""
while ...:
...
tah_hrace(...)
...
# Puštění hry!
piskvorky1d()
Když tenhle modul naimportuješ, Python v něm postupně, odshora dolů, provede všechny příkazy.
První příkaz, import
, jen zpřístupní nějaké proměnné a funkce;
je-li importovaný modul správně napsaný, nemá vedlejší účinek.
Definice funkcí (příkazy def
a všechno v nich) podobně jen definují funkce.
Ale zavoláním funkce piskvorky1d
se spustí hra:
funkce piskvorky1d
zavolá funkci tah_hrace()
a ta zavolá input()
.
Importuješ-li tenhle modul z testů, input
selže a import se nepovede.
A kdybys modul importovala odjinud – například bys chtěla funkci
tah
použít v nějaké jiné hře – uživatel si bude muset v rámci importu
zahrát Piškvorky!
Volání funkce piskvorky1d
je vedlejší efekt, a je potřeba ho odstranit.
No jo, ale po takovém odstranění
už nejde jednoduše spustit hra! Co s tím?
Můžeš na to vytvořit nový modul.
Pojmenuj ho hra.py
a dej do něj jenom to odstraněné volání:
import piskvorky
piskvorky.piskvorky1d()
Tenhle modul nebudeš moci testovat (protože nepřímo volá funkci input
),
ale můžeš ho spustit, když si budeš chtít zahrát.
Protože k němu nemáš napsané testy, nepoznáš
z nich, když se takový spouštěcí modul rozbije.
Měl by být proto nejjednodušší – jeden import a jedno volání.
Původní modul teď můžeš importovat bez obav – ať už z testů nebo z jiných modulů. Test může vypadat třeba takhle:
import piskvorky
def test_tah_na_prazdne_pole():
pole = piskvorky.tah_pocitace('--------------------')
assert len(pole) == 20
assert pole.count('x') == 1
assert pole.count('-') == 19
Testům, které kontrolují že se program za správných podmínek chová správně, se říká pozitivní testy. Můžeš ale testovat i reakci programu na špatné nebo neočekávané podmínky.
Testy, které kontrolují reakci na „špatný“ vstup,
se jmenují negativní testy.
Můžou kontrolovat nějaký negativní výsledek (např.
že volání jako cislo_je_sude(7)
vrátí False
),
a nebo to, že nastane „rozumná“ výjimka.
Například funkce tah_pocitace
by měla způsobit
chybu (třeba ValueError
), když je herní pole už plné.
Vyvolat výjimku je mnohem lepší než alternativy, např. kdyby takové volání „tiše“ – bez oznámení – zablokovalo celý program. Když kód pak použiješ ve větším programu, můžeš si být jistá, že při špatném volání dostaneš srozumitelnou chybu – tedy takovou, která se co nejsnadněji opravuje.
Na otestování výjimky to použij příkaz with
a funkci raises
naimportovanou
z modulu pytest
.
Jak příkaz with
přesně funguje, se dozvíme později;
teď stačí říct, že ověří, že odsazený blok kódu
pod ním vyvolá danou výjimku:
import pytest
import piskvorky
def test_tah_chyba():
with pytest.raises(ValueError):
piskvorky.tah_pocitace('oxoxoxoxoxoxoxoxoxox')
{ "data": { "sessionMaterial": { "id": "session-material:2018/pyladies-brno-podzim-exp:iterable:3", "title": "TestovánĂ", "html": "\n \n \n\n <h1>Testování</h1>\n<p>Programátorská práce nespočívá jen v tom, program napsat.\nDůležité je si i ověřit, že opravdu funguje (a případně ho pak opravit).\nOvěřování, že program funguje, se říká <em>testování</em>.</p>\n<p>Zatím jsi asi svoje programy testovala tak, že jsi\nje zkusila spustit, něco zadala a podívala se,\njestli jsou výsledky v pořádku.\nU větších programů, které budou mít více a více\nmožností, ale bude těžší a těžší takhle zkontrolovat,\njestli všechny ty možnosti fungují, jak mají.</p>\n<p>Proto si programátoři, místo aby program zkoušeli ručně, píšou jiné programy,\nkteré testují jejich výtvory za ně.</p>\n<p><em>Automatické testy</em> jsou funkce, které\nzkontrolují, že náš program funguje správně.\nSpuštěním testů můžeš kdykoli ověřit, že kód funguje.\nHlavní výhoda je, že když v otestovaném kódu\nv budoucnu uděláš nějakou změnu,\ntesty ověří, že jsi nerozbila nic, co dříve\nfungovalo.</p>\n<h2>Instalace knihovny pytest</h2>\n<p>Zatím jsme v kurzu pracovaly s tím, co se instaluje\nse samotným Pythonem – s moduly jako <code>math</code> a <code>turtle</code>.\nKromě takových modulů ale existuje ale velká spousta\ndalších <em>knihoven</em>, které nejsou přímo v Pythonu, ale dají se doinstalovat\na používat.</p>\n<p>Na testy je v samotném Pythonu zabudovaná knihovna <code>unittest</code>.\nTa je ale celkem složitá na použití, proto ji my používat nebudeme.\nNainstalujeme si knihovnu <code>pytest</code>, která se používá\nmnohem jednodušeji a je velice populární.</p>\n<p>Knihovny se instalují do aktivního virtuálního prostředí.\nJak se dělá a spouští virtuální prostředí\nses naučila při <a href=\"/2018/pyladies-brno-podzim-exp/beginners/install/\">instalaci Pythonu</a>,\nale teprve teď to začíná být opravdu důležité.\nUjisti se, že máš virtuální prostředí aktivované.</p>\n<p>Potom zadej následující příkaz.\n(Je to příkaz příkazové řádky, podobně jako\n<code>cd</code> nebo <code>mkdir</code>; nezadávej ho do Pythonu.)</p>\n<div class=\"highlight\"><pre><span></span><span class=\"gp\">(venv)$ </span>python -m pip install pytest\n</pre></div><div class=\"admonition note\"><p class=\"admonition-title\">Co to znamená?</p>\n<p><code>python -m pip</code> zavolá Python s tím, že má pustit modul\n<code>pip</code>. Tento modul umí instalovat nebo\nodinstalovávat knihovny.\n(Jestli si pamatuješ vytváření virtuálního prostředí, použila jsi tam\npříkaz <code>python -m venv</code> – modul <code>venv</code> umí vytvářet virtuální prostředí.)\nNo a slova <code>install pytest</code> říkají Pipu, že má nainstalovat <code>pytest</code>.</p>\n<p>Nápověda k použití Pipu se dá vypsat pomocí příkazu\n<code>python -m pip --help</code>.</p>\n</div><div class=\"admonition warning\"><p class=\"admonition-title\">Pro Windows</p>\n<p>Jsi-li na Windows, od této lekce začne být důležité\nspouštět pythonní programy pomocí <code>python program.py</code>, ne jen\n<code>program.py</code>.\nAčkoli se v těchto materiálech všude používá <code>python</code> na začátku, zatím\nmohlo všechno fungovat i bez toho.\nProgram se ale bez příkazu <code>python</code> může spustit v jiném Pythonu,\nnež v tom z virtuálního prostředí – a tam <code>pytest</code> nebude k dispozici.</p>\n</div><h2>Psaní testů</h2>\n<p>Nejdříve si testování ukážeme na jednoduchém příkladu.\nTady je funkce <code>secti</code>, která umí sečíst\ndvě čísla, a další funkce, která testuje, jestli se\n<code>secti</code> pro určité hodnoty\nchová správně.</p>\n<p>Kód si opiš do souboru <code>test_secteni.py</code>,\nv novém prázdném adresáři.\nPro <code>pytest</code> je (ve výchozím nastavení)\ndůležité, aby jména jak souborů s testy, tak\nsamotných testovacích funkcí, začínala na\n<code>test_</code>.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">secti</span><span class=\"p\">(</span><span class=\"n\">a</span><span class=\"p\">,</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n <span class=\"k\">return</span> <span class=\"n\">a</span> <span class=\"o\">+</span> <span class=\"n\">b</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">test_secti</span><span class=\"p\">():</span>\n <span class=\"k\">assert</span> <span class=\"n\">secti</span><span class=\"p\">(</span><span class=\"mi\">1</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"mi\">3</span>\n</pre></div><p>Co se v té testovací funkci děje?</p>\n<p>Příkaz <code>assert</code> vyhodnotí výraz za ním\na pokud výsledek není pravdivý, vyvolá výjimku,\nkterá způsobí, že test selže.\nMůžeš si představit, že <code>assert a == b</code> dělá následující:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">if</span> <span class=\"ow\">not</span> <span class=\"p\">(</span><span class=\"n\">a</span> <span class=\"o\">==</span> <span class=\"n\">b</span><span class=\"p\">):</span>\n <span class=\"k\">raise</span> <span class=\"ne\">AssertionError</span><span class=\"p\">(</span><span class=\"s1\">'Test selhal!'</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition note\"><p>Zatím <code>assert</code> nepoužívej jinde než v testovacích funkcích.\nV „normálním” kódu má <code>assert</code> vlastnosti,\ndo kterých teď nebudeme zabředávat.</p>\n</div><h2>Spouštění testů</h2>\n<p>Testy se spouští zadáním příkazu\n<code>python -m pytest -v</code> následovaným názvem souboru s testy.\nTedy v překladu: <strong>Python</strong>e, pusť\n<strong>m</strong>odul <strong>pytest</strong>,\nv „ukecaném” režimu (angl. <strong>v</strong>erbose) nad zadaným souborem.</p>\n<div class=\"highlight\"><pre><code>$ python -m pytest -v test_secteni.py\n<span style=\"font-weight: bold\">============= test session starts =============</span>\nplatform linux -- Python 3.6.0, pytest-3.0.6, py-1.4.32, pluggy-0.4.0 -- env/bin/python\ncachedir: .cache\nrootdir: naucse, inifile: \n<span style=\"font-weight: bold\">collecting ...</span> collected 1 items\n\ntest_secteni.py::test_secti <span style=\"color: #00aa00\">PASSED</span>\n\n<span style=\"color: #00aa00\">============= 1 passed in 0.00 seconds =============</span></code></pre></div><p>Tento příkaz projde zadaný soubor, zavolá v něm všechny funkce,\njejichž jméno začíná na <code>test_</code>, a ověří, že nevyvolají žádnou\nvýjimku – typicky výjimku z příkazu <code>assert</code>.\nPokud výjimka nastane, dá to <code>pytest</code> velice červeně\nnajevo a přidá několik informací, které můžou\nusnadnit nalezení a opravu chyby.</p>\n<div class=\"admonition note\"><p>Argument s názvem souboru můžeme vynechat: <code>python -m pytest -v</code>\nV takovém případě <code>pytest</code> projde aktuální adresář a spustí testy\nze všech souborů, jejichž jméno začíná na <code>test_</code>. Místo souboru\nlze též uvést adresář a <code>pytest</code> vyhledá testy v něm.</p>\n</div><p>Zkus si změnit funkci <code>secti</code> (nebo její test) a podívat se,\njak to vypadá když test „neprojde“.</p>\n<h2>Testovací moduly</h2>\n<p>Testy se většinou nepíšou přímo ke kódu,\nale do souboru vedle.\nJe to tak přehlednější a taky to pak zjednodušuje\ndistribuci – předávání kódu někomu, kdo ho chce\njen spustit a testy nepotřebuje.</p>\n<p>Rozděl soubor s testem sečítání: funkci <code>secti</code> přesuň do modulu <code>secteni.py</code>,\na v <code>test_secteni.py</code> nech jenom test.\nDo <code>test_secteni.py</code> pak na začátek přidej <code>from secteni import secti</code>,\naby byla funkce testu k dispozici.</p>\n<p>Test by měl opět projít.</p>\n<h2>Spouštěcí moduly</h2>\n<p>Automatické testy musí projít „bez dozoru“.\nV praxi se často automaticky spouští, případné chyby se automaticky\noznamují (např. e-mailem) a fungující kód se automaticky\nzačne používat dál (nebo se rovnou vydá zákazníkům).</p>\n<p>Co to znamená pro nás?\nFunkce <code>input</code> v testech nefunguje. Nemá koho by se zeptala; „za klávesnicí“\nnemusí nikdo sedět.</p>\n<p>To může někdy „ztěžovat práci“. Ukážeme si to na složitějším projektu:\nna 1D piškvorkách.</p>\n<div class=\"admonition note\"><p>Nemáš-li hotové 1D piškvorky, následující sekce budou jen teorietické.\nUčíš-li se z domu, dodělej si Piškvorky než budeš pokračovat dál!\nZadání najdeš (prozatím)\nv <a href=\"http://pyladies.cz/v1/s004-strings/handout/handout4.pdf\">projektech pro PyLadies</a>\nna straně 2.</p>\n</div><p>Kód pro 1D Piškvorky může rámcově vypadat zhruba takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">random</span> <span class=\"c1\"># (příp. import jiných věci, které budou potřeba)</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">tah</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">,</span> <span class=\"n\">cislo_policka</span><span class=\"p\">,</span> <span class=\"n\">symbol</span><span class=\"p\">):</span>\n <span class=\"sd\">"""Vrátí pole s daným symbolem umístěným na danou pozici"""</span>\n <span class=\"o\">...</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">tah_hrace</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">):</span>\n <span class=\"sd\">"""Zeptá se hráče kam chce hrát a vrátí pole se zaznamenaným tahem"""</span>\n <span class=\"o\">...</span>\n <span class=\"nb\">input</span><span class=\"p\">(</span><span class=\"s1\">'Kam chceš hrát? '</span><span class=\"p\">)</span>\n <span class=\"o\">...</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">piskvorky1d</span><span class=\"p\">():</span>\n <span class=\"sd\">"""Spustí hru</span>\n\n<span class=\"sd\"> Vytvoří hrací pole a střídavě volá tah_hrace a tah_pocitace</span>\n<span class=\"sd\"> dokud někdo nevyhraje"""</span>\n <span class=\"k\">while</span> <span class=\"o\">...</span><span class=\"p\">:</span>\n <span class=\"o\">...</span>\n <span class=\"n\">tah_hrace</span><span class=\"p\">(</span><span class=\"o\">...</span><span class=\"p\">)</span>\n <span class=\"o\">...</span>\n\n<span class=\"c1\"># Puštění hry!</span>\n<span class=\"n\">piskvorky1d</span><span class=\"p\">()</span>\n</pre></div><p>Když tenhle modul naimportuješ, Python v něm postupně, odshora dolů,\nprovede všechny příkazy.</p>\n<p>První příkaz, <code>import</code>, jen zpřístupní nějaké proměnné a funkce;\nje-li importovaný modul správně napsaný, nemá vedlejší účinek.\nDefinice funkcí (příkazy <code>def</code> a všechno v nich) podobně jen definují funkce.\nAle zavoláním funkce <code>piskvorky1d</code> se spustí hra:\nfunkce <code>piskvorky1d</code> zavolá funkci <code>tah_hrace()</code> a ta zavolá <code>input()</code>.</p>\n<p>Importuješ-li tenhle modul z testů, <code>input</code> selže a import se nepovede.</p>\n<div class=\"admonition note\"><p>A kdybys modul importovala odjinud – například bys chtěla funkci\n<code>tah</code> použít v nějaké jiné hře – uživatel si bude muset v rámci importu\nzahrát Piškvorky!</p>\n</div><p>Volání funkce <code>piskvorky1d</code> je vedlejší efekt, a je potřeba ho odstranit.\nNo jo, ale po takovém odstranění\nuž nejde jednoduše spustit hra! Co s tím?</p>\n<p>Můžeš na to vytvořit nový modul.\nPojmenuj ho <code>hra.py</code> a dej do něj jenom to odstraněné volání:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">piskvorky</span>\n\n<span class=\"n\">piskvorky</span><span class=\"o\">.</span><span class=\"n\">piskvorky1d</span><span class=\"p\">()</span>\n</pre></div><p>Tenhle modul nebudeš moci testovat (protože nepřímo volá funkci <code>input</code>),\nale můžeš ho spustit, když si budeš chtít zahrát.\nProtože k němu nemáš napsané testy, nepoznáš\nz nich, když se takový spouštěcí modul rozbije.\nMěl by být proto nejjednodušší – jeden import a jedno volání.</p>\n<p>Původní modul teď můžeš importovat bez obav – ať už z testů nebo z jiných\nmodulů.\nTest může vypadat třeba takhle:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">piskvorky</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">test_tah_na_prazdne_pole</span><span class=\"p\">():</span>\n <span class=\"n\">pole</span> <span class=\"o\">=</span> <span class=\"n\">piskvorky</span><span class=\"o\">.</span><span class=\"n\">tah_pocitace</span><span class=\"p\">(</span><span class=\"s1\">'--------------------'</span><span class=\"p\">)</span>\n <span class=\"k\">assert</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">pole</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"mi\">20</span>\n <span class=\"k\">assert</span> <span class=\"n\">pole</span><span class=\"o\">.</span><span class=\"n\">count</span><span class=\"p\">(</span><span class=\"s1\">'x'</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"mi\">1</span>\n <span class=\"k\">assert</span> <span class=\"n\">pole</span><span class=\"o\">.</span><span class=\"n\">count</span><span class=\"p\">(</span><span class=\"s1\">'-'</span><span class=\"p\">)</span> <span class=\"o\">==</span> <span class=\"mi\">19</span>\n</pre></div><h2>Pozitivní a negativní testy</h2>\n<p>Testům, které kontrolují že se program za správných podmínek chová správně,\nse říká <em>pozitivní testy</em>.\nMůžeš ale testovat i reakci programu na špatné nebo neočekávané podmínky.</p>\n<p>Testy, které kontrolují reakci na „špatný“ vstup,\nse jmenují <em>negativní testy</em>.\nMůžou kontrolovat nějaký negativní výsledek (např.\nže volání jako <code>cislo_je_sude(7)</code> vrátí <code>False</code>),\na nebo to, že nastane „rozumná“ výjimka.</p>\n<p>Například funkce <code>tah_pocitace</code> by měla způsobit\nchybu (třeba <code>ValueError</code>), když je herní pole už plné.</p>\n<div class=\"admonition note\"><p>Vyvolat výjimku je mnohem lepší než alternativy, např. kdyby takové volání\n„tiše“ – bez oznámení – zablokovalo celý program.\nKdyž kód pak použiješ ve větším programu,\nmůžeš si být jistá, že při špatném volání\ndostaneš srozumitelnou chybu – tedy takovou,\nkterá se co nejsnadněji opravuje.</p>\n</div><p>Na otestování výjimky to použij příkaz <code>with</code> a funkci <code>raises</code> naimportovanou\nz modulu <code>pytest</code>.\nJak příkaz <code>with</code> přesně funguje, se dozvíme později;\nteď stačí říct, že ověří, že odsazený blok kódu\npod ním vyvolá danou výjimku:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">pytest</span>\n\n<span class=\"kn\">import</span> <span class=\"nn\">piskvorky</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">test_tah_chyba</span><span class=\"p\">():</span>\n <span class=\"k\">with</span> <span class=\"n\">pytest</span><span class=\"o\">.</span><span class=\"n\">raises</span><span class=\"p\">(</span><span class=\"ne\">ValueError</span><span class=\"p\">):</span>\n <span class=\"n\">piskvorky</span><span class=\"o\">.</span><span class=\"n\">tah_pocitace</span><span class=\"p\">(</span><span class=\"s1\">'oxoxoxoxoxoxoxoxoxox'</span><span class=\"p\">)</span>\n</pre></div>\n\n\n " } } }