Obsah
1. Testování aplikací s využitím nástroje Hypothesis
3. Malá odbočka – Hoarého logika
4. Jednoduchý příklad – implementace bublinkového řazení
5. Vytvoření klasických jednotkových testů
6. Použití Hypothesis a orákula
7. Zdánlivé vylepšení algoritmu bublinkového řazení
8. Zavedení chyby do implementace algoritmu
9. Výsledek testů vytvořených nástrojem Hypothesis
10. Specifikace minimální a maximální velikosti vstupů
11. Vylepšení podmínky při neexistenci referenční implementace
12. Jednoduchá šifra typu ROT13
13. Otestování jednoduché šifry
14. Zanesení chyby do algoritmu šifrování
15. Přesnější specifikace podoby generovaných textových dat
18. Repositář s demonstračními příklady
19. Předchozí články s tématem testování (nejenom) v Pythonu
1. Testování aplikací s využitím nástroje Hypothesis
V předchozí části seriálu o tvorbě testů s využitím programovacího jazyka Python jsme si řekli, jakým způsobem je možné zmenšit „stavový prostor“ testované jednotky (tedy funkce, metody či celého objektu) a tím pádem i zmenšit počet testů, které je nutné explicitně vytvořit. Pro zmenšení oblasti stavového prostoru jsme použili nástroj Mypy, který umožňuje zkontrolovat (nepovinné) typové deklarace, jenž mohou být ve zdrojových kódech Pythonu použity. Jedná se o relativně novou vlastnost zavedenou v rámci Pythonu 3.5 a později ještě lépe stabilizovanou (verze 3.5 sice vyšla již před pěti lety, ovšem na příkladu Pythonu 2 je patrné, že nové verze nebývají vždy přijímány příliš rychle). Existuje ovšem i opačný přístup k tvorbě testů – nechat si na základě zadaných pravidel a vzorů nechat testy automaticky vygenerovat, ideálně takovým způsobem, aby se ve vygenerovaných testech projevily i různé mezní případy. A právě na tomto přístupu je založen nástroj nazvaný Hypothesis, kterým se budeme zabývat dnes.
Nástroj Hypothesis je ovšem velmi užitečný i při dalších činnostech, nejenom jako pomocník při tvorbě testů. Díky tomu, že lze použít takzvané orákulum (viz navazující kapitoly), je umožněno, aby byl Hypothesis použit například při refaktoringu či při optimalizacích algoritmů – postačuje totiž mít dvě implementace algoritmu (neoptimalizovanou/nerefaktorovanou a novou) a ty si nechat navzájem prověřit, pochopitelně opět s využitím mezních případů. Podle mého názoru se jedná o velmi užitečný a především praktický přístup.
Před pročítáním dalších kapitol je vhodné si Hypothesis nainstalovat, a to klasicky s využitím nástroje pip či pip3:
$ pip3 install --user hypothesis Collecting hypothesis Downloading https://files.pythonhosted.org/packages/99/27/4a3fd8eb6e121e6769bf83a3c1647bc1daab586ac7c70fcf93c4756c51f3/hypothesis-5.16.0-py3-none-any.whl (294kB) Collecting sortedcontainers<3.0.0,>=2.1.0 (from hypothesis) Downloading https://files.pythonhosted.org/packages/13/f3/cf85f7c3a2dbd1a515d51e1f1676d971abe41bba6f4ab5443240d9a78e5b/sortedcontainers-2.1.0-py2.py3-none-any.whl Collecting attrs>=19.2.0 (from hypothesis) Downloading https://files.pythonhosted.org/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl Installing collected packages: sortedcontainers, attrs, hypothesis Successfully installed attrs-19.3.0 hypothesis-5.16.0 sortedcontainers-2.1.0
2. Jak testování probíhá
Pojďme si nyní ukázat, jak vlastně testování s využitím nástroje Hypothesis probíhá. Nejdříve z pohledu vývojáře (či obecněji tvůrce testů):
- Uživatel nejdříve popíše, jak má vypadat validní vstup či vstupy do testované jednotky.
- Dále napíše test, který by pro tento vstup měl projít bez chyby.
Z pohledu nástroje Hypothesis:
- Automaticky vytvoří test pro jednotlivé testovací případy založené na popisu validního vstupu.
- Spustí vytvořený test pro jednotlivé testovací případy.
- Sesbírá vstupy u těch testů, které z nějakého důvodu zhavarovaly.
- Nakonec se pokusí najít minimální vstup způsobující chybu.
Při generování testovacích dat se používají takzvané strategie. Ty si můžeme snadno odzkoušet, a to ještě před vytvořením testů. Na samotné strategie se totiž můžeme dívat jako na generátory „fuzzy“ dat se zadanými vlastnosti – typem, lze použít filtraci, funkci map atd.
Vygenerování deseti sad testovacích dat popsaných takto: „seznam obsahující celá čísla“:
from hypothesis.strategies import lists, integers g = lists(integers()) for _ in range(10): print(g.example())
Příklad výsledků:
[-74, 1304181783, -1693807871, -10980, -4652, -16732] [-30506, 23947, 61] [0] [101, -27349, 7493] [-16316, -121, 21307] [] [] [0] [0] [339202945]
Vygenerování deseti sad testovacích dat, tentokrát řetězců o minimální délce pěti znaků a maximální délce deseti znaků:
from hypothesis.strategies import text g = text(min_size=5, max_size=10) for _ in range(10): print(g.example())
Generování textových vstupů lze ovšem dále omezit, například na tisknutelné znaky:
from string import printable from hypothesis.strategies import text g = text(printable, min_size=5, max_size=10) for _ in range(10): print(g.example())
3. Malá odbočka – Hoarého logika
Sice to tak nemusí na první pohled vypadat, ale jak Hypothesis, tak i obecnější BDD (Behavior-driven development) jsou založeny na Hoarého logice (nebo též Hoarého trojici). Ta je založena na trojici {P}C{Q}, kde se symbolem P označuje podmínka platná před spuštěním testovaného bloku, Q je podmínka, která musí platit po dokončení bloku a C představuje spuštěný (testovaný) blok.
Konkrétně může test vypadat následovně:
@given(lists(integers())) def test_bubble_sort(alist): l = bubble_sort(alist) assert l == sorted(alist)
P je představován prvním řádkem s dekorátorem @given, C je spouštěný blok, tedy třetí řádek a Q je podmínka zapsaná na řádku posledním.
Ještě lépe je trojice vidět na BDD testech, kde jsou jednotlivé prvky trojice zapsány větami za slovy Given, When a Then:
Feature: Adder test Scenario: Check the function add() Given The function add is callable When I call function add with arguments 1 and 2 Then I should get 3 as a result
4. Jednoduchý příklad – implementace bublinkového řazení
Použití nástroje Hypothesis si nejdříve ukážeme na jednoduchém (a dosti naivně implementovaném) algoritmu pro bublinkové řazení. Pochopitelně se nejedná o algoritmus vhodný pro praktické nasazení (snad kromě speciálních případů), ovšem svému účelu v kontextu tohoto článku vyhovuje. Naivní bublinkové třídění bez detekce toho, že je seznam již setříděn, může vypadat takto:
"""Implementace naivního algoritmu bublinkového řazení.""" def bubble_sort(alist): """Implementace naivního algoritmu bublinkového řazení.""" for i in range(len(alist)-1, 0, -1): for j in range(0, i): if alist[j] > alist[j+1]: alist[j], alist[j+1] = alist[j+1], alist[j] return alist if __name__ == '__main__': print(bubble_sort([])) print(bubble_sort([1])) print(bubble_sort([1, 2])) print(bubble_sort([2, 1])) print(bubble_sort([1, 2, 3, 4])) print(bubble_sort([1, 2, 4, 3])) print(bubble_sort([1, 4, 3, 2])) print(bubble_sort([4, 3, 2, 1])) print(bubble_sort([1, 5, 4, 3, 2])) print(bubble_sort([5, 4, 3, 2, 1]))
Součástí modulu je i několik příkladů použití funkce bubble_sort, takže si můžeme ověřit její chování:
[] [1] [1, 2] [1, 2] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
5. Vytvoření klasických jednotkových testů
Samozřejmě je možné si napsat běžné jednotkové testy, které chování funkce bubble_sort ověří, ovšem pouze na zadaných datech:
"""Test naivního algoritmu bublinkového řazení.""" from bubble_sort import bubble_sort def test_bubble_sort_empty_input(): """Test naivního algoritmu bublinkového řazení.""" assert bubble_sort([]) == [] def test_bubble_sort_one_item(): """Test naivního algoritmu bublinkového řazení.""" assert bubble_sort([1]) == [1] def test_bubble_sort_two_items(): """Test naivního algoritmu bublinkového řazení.""" assert bubble_sort([1, 2]) assert bubble_sort([2, 1]) def test_bubble_sort_four_items(): """Test naivního algoritmu bublinkového řazení.""" assert bubble_sort([1, 2, 3, 4]) assert bubble_sort([1, 2, 4, 3]) assert bubble_sort([1, 4, 3, 2]) assert bubble_sort([4, 3, 2, 1]) def test_bubble_sort_five_items(): """Test naivního algoritmu bublinkového řazení.""" assert bubble_sort([1, 5, 4, 3, 2]) assert bubble_sort([5, 4, 3, 2, 1])
Takto naprogramované testy můžeme spustit běžným způsobem:
$ pytest -v
S výsledky:
============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_1/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_1 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 5 items bubble_sort_test.py::test_bubble_sort_empty_input PASSED [ 20%] bubble_sort_test.py::test_bubble_sort_one_item PASSED [ 40%] bubble_sort_test.py::test_bubble_sort_two_items PASSED [ 60%] bubble_sort_test.py::test_bubble_sort_four_items PASSED [ 80%] bubble_sort_test.py::test_bubble_sort_five_items PASSED [100%] ============================== 5 passed in 0.02s ===============================
6. Použití Hypothesis a orákula
Nyní se pokusíme pro otestování algoritmu bublinkového řazení použít nástroj Hypothesis. V tomto případě bude testovaná podmínka jednoduchá, a to z toho důvodu, že máme k dispozici vhodné orákulum, což je jen květnaté označení mechanismu určujícího, zda testovaná funkce prošla testem či nikoli. V případě algoritmu řazení je orákulem jiný – již prověřený – algoritmus řazení a ten existuje ve standardní knihovně jazyka Python pod jménem sorted. Celý test lze zapsat takto:
"""Test naivního algoritmu bublinkového řazení.""" from hypothesis import given from hypothesis.strategies import lists, integers from bubble_sort import bubble_sort @given(lists(integers())) def test_bubble_sort(alist): assert bubble_sort(alist) == sorted(alist)
Celý zápis je vlastně velmi jednoduchý. Po importech nutných funkcí a dekorátorů následuje zápis testu podle specifikací vyžadovaných nástrojem Pytest. V něm je použit dekorátor, který určuje, že testovací funkce test_bubble_sort bude postupně volána a bude jí předáván vygenerovaný seznam celých čísel. Ve funkci ověříme, zda výsledek řazení pomocí bubble_sort odpovídá výsledku řazení pomocí ověřeného algoritmu (sorted).
Takto zapsaný test opět spustíme standardním způsobem:
$ pytest -v
S výsledky:
============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_2/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_2 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort PASSED [100%] ============================== 1 passed in 0.23s ===============================
Můžeme si nechat zobrazit i podrobnější statistiku o operacích provedených nástrojem Hypothesis:
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_2/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_2 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort PASSED [100%] ============================ Hypothesis Statistics ============================= bubble_sort_test.py::test_bubble_sort: - during reuse phase (0.00 seconds): - Typical runtimes: < 1ms, ~ 54% in data generation - 2 passing examples, 0 failing examples, 0 invalid examples - during generate phase (0.18 seconds): - Typical runtimes: 0-1 ms, ~ 75% in data generation - 98 passing examples, 0 failing examples, 0 invalid examples - Stopped because settings.max_examples=100 ============================== 1 passed in 0.21s ===============================
7. Zdánlivé vylepšení algoritmu bublinkového řazení
V minulosti existovala celá řada pokusů o vylepšení algoritmu bublinkového řazení, aby se zlepšila jeho dosti špatná časová složitost O(n2). Existují samozřejmě ještě horší algoritmy, například Bogosort, ovšem ty jsou schválně navrženy takovým způsobem, aby měly špatnou časovou složitost. Jedním z vylepšení (či lépe řečeno pseudovylepšení) je použití čítače, který testuje, kolik operací se maximálně provede. Ve skutečnosti se jedná o kód, který složitost nevylepší, ale jeho přidání nám umožní později do algoritmu zanést chybu:
"""Implementace naivního algoritmu bublinkového řazení.""" def bubble_sort(alist): """Implementace naivního algoritmu bublinkového řazení.""" cnt = len(alist)*(len(alist)-1)/2 for i in range(len(alist)-1, 0, -1): for j in range(0, i): if alist[j] > alist[j+1]: alist[j], alist[j+1] = alist[j+1], alist[j] cnt -= 1 if cnt == 0: return alist return alist if __name__ == '__main__': print(bubble_sort([])) print(bubble_sort([1])) print(bubble_sort([1, 2])) print(bubble_sort([2, 1])) print(bubble_sort([1, 2, 3, 4])) print(bubble_sort([1, 2, 4, 3])) print(bubble_sort([1, 4, 3, 2])) print(bubble_sort([4, 3, 2, 1])) print(bubble_sort([1, 5, 4, 3, 2])) print(bubble_sort([5, 4, 3, 2, 1]))
Ověření funkčnosti (když už ne vylepšení) upraveného algoritmu:
============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_3/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_3 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort PASSED [100%] ============================== 1 passed in 0.18s ===============================
8. Zavedení chyby do implementace algoritmu
Nyní do algoritmu řazení schválně zaneseme chybu – ukončíme celý proces o jeden krok dříve, což může vést k tomu, že v některých případech nebude seznam zcela setříděn (typicky pokud je počáteční seznam otočený):
cnt = len(alist)*(len(alist)-1)/2-1
Upravený (resp. přesněji řečeno rozbitý) zdrojový kód:
"""Implementace naivního algoritmu bublinkového řazení.""" def bubble_sort(alist): """Implementace naivního algoritmu bublinkového řazení.""" cnt = len(alist)*(len(alist)-1)/2-1 for i in range(len(alist)-1, 0, -1): for j in range(0, i): if alist[j] > alist[j+1]: alist[j], alist[j+1] = alist[j+1], alist[j] cnt -= 1 if cnt == 0: return alist return alist if __name__ == '__main__': print(bubble_sort([])) print(bubble_sort([1])) print(bubble_sort([1, 2])) print(bubble_sort([2, 1])) print(bubble_sort([1, 2, 3, 4])) print(bubble_sort([1, 2, 4, 3])) print(bubble_sort([1, 4, 3, 2])) print(bubble_sort([4, 3, 2, 1])) print(bubble_sort([1, 5, 4, 3, 2])) print(bubble_sort([5, 4, 3, 2, 1]))
Z výsledků je patrné, že se skutečně v některých případech sekvence neseřadí. Tyto výsledky jsou zvýrazněny:
[] [1] [1, 2] [1, 2] [1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4] [2, 1, 3, 4] [1, 2, 3, 4, 5] [2, 1, 3, 4, 5]
9. Výsledek testů vytvořených nástrojem Hypothesis
A nyní přijde na řadu zjištění, jak dobře (či jak špatně) dokáže námi rozbitý algoritmus otestovat nástroj Hypothesis. Samotné testy ponecháme beze změny:
"""Test naivního algoritmu bublinkového řazení.""" from hypothesis import given from hypothesis.strategies import lists, integers from bubble_sort import bubble_sort @given(lists(integers())) def test_bubble_sort(alist): assert bubble_sort(alist) == sorted(alist)
Ovšem v ideálním případě by měla být chyba odhalena i s tímto jednoduchým (v podstatě třířádkovým) testem:
$ pytest -v --hypothesis-show-statistics
Chyba je odhalena relativně rychle, a to konkrétně na sekvenci se třemi prvky:
============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_4/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_4 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort FAILED [100%] =================================== FAILURES =================================== _______________________________ test_bubble_sort _______________________________ @given(lists(integers())) > def <strong>test_bubble_sort</strong>(alist): bubble_sort_test.py:10: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ alist = [0, -1, 0] @given(lists(integers())) def <strong>test_bubble_sort</strong>(alist): > assert bubble_sort(alist) == sorted(alist) E assert [0, -1, 0] == [-1, 0, 0] E At index 0 diff: 0 != -1 E Full diff: E - [-1, 0, 0] E + [0, -1, 0] bubble_sort_test.py:11: AssertionError ---------------------------------- Hypothesis ---------------------------------- Falsifying example: test_bubble_sort( alist=[0, 0, -1], ) ============================ Hypothesis Statistics ============================= bubble_sort_test.py::test_bubble_sort: - during reuse phase (0.04 seconds): - Typical runtimes: 1-23 ms, ~ 34% in data generation - 0 passing examples, 10 failing examples, 0 invalid examples - Found 1 failing example in this phase - during shrink phase (0.04 seconds): - Typical runtimes: 0-1 ms, ~ 68% in data generation - 14 passing examples, 1 failing examples, 4 invalid examples - Tried 19 shrinks of which 0 were successful - Stopped because nothing left to do =========================== short test summary info ============================ FAILED bubble_sort_test.py::test_bubble_sort - assert [0, -1, 0] == [-1, 0, 0] ============================== 1 failed in 0.11s ===============================
10. Specifikace minimální a maximální velikosti vstupů
V testovacích strategiích je možné jednoduše zvolit minimální, popř. maximální velikosti generovaných testovacích dat. To je užitečné například při přípravě seznamů, které mají být seřazeny. Nemusíme totiž začínat testování od seznamu s nulovou délkou, ale instruovat Hypothesis, aby začal vytvářet seznamy o délce 5 či o větší délce. Podívejme se na upravený příklad:
"""Test naivního algoritmu bublinkového řazení.""" from hypothesis import given from hypothesis.strategies import lists, integers from bubble_sort import bubble_sort @given(lists(integers(), min_size=5)) def test_bubble_sort(alist): assert bubble_sort(alist) == sorted(alist)
Nyní testy (podle očekávání) opět detekují chybu, ale v tomto případě u seznamu s pěti prvky:
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_5/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_5 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort FAILED [100%] =================================== FAILURES =================================== _______________________________ test_bubble_sort _______________________________ @given(lists(integers(), min_size=5)) > def test_bubble_sort(alist): bubble_sort_test.py:10: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ alist = [0, -1, 0, 0, 0] @given(lists(integers(), min_size=5)) def test_bubble_sort(alist): > assert bubble_sort(alist) == sorted(alist) E assert [0, -1, 0, 0, 0] == [-1, 0, 0, 0, 0] E At index 0 diff: 0 != -1 E Full diff: E - [-1, 0, 0, 0, 0] E ? --- E + [0, -1, 0, 0, 0] E ? +++ bubble_sort_test.py:11: AssertionError ---------------------------------- Hypothesis ---------------------------------- Falsifying example: test_bubble_sort( alist=[0, 0, 0, 0, -1], ) ============================ Hypothesis Statistics ============================= bubble_sort_test.py::test_bubble_sort: - during reuse phase (0.04 seconds): - Typical runtimes: 1-23 ms, ~ 37% in data generation - 0 passing examples, 10 failing examples, 0 invalid examples - Found 1 failing example in this phase - during shrink phase (0.06 seconds): - Typical runtimes: < 1ms, ~ 87% in data generation - 5 passing examples, 1 failing examples, 37 invalid examples - Tried 43 shrinks of which 0 were successful - Stopped because nothing left to do =========================== short test summary info ============================ FAILED bubble_sort_test.py::test_bubble_sort - assert [0, -1, 0, 0, 0] == [-1... ============================== 1 failed in 0.14s ===============================
11. Vylepšení podmínky při neexistenci referenční implementace
Předchozí testy byly založeny na tom, že jsme dokázali najít to nejjednodušší možné orákulum – referenční implementaci algoritmu řazení. To nám umožnilo snadnou kontrolu našeho algoritmu vůči referenční implementaci:
assert bubble_sort(alist) == sorted(alist)
Předpokládejme nyní, že referenční implementace není z nějakého důvodu k dispozici. Testy je tedy nutné přepsat jiným způsobem, a to tak, že se pokusíme nalézt nějakou podmínku, která musí platit pro setříděný seznam. A tato podmínka je zřejmá – každý další prvek seznamu musí být větší nebo roven prvku předchozímu, tedy:
assert all(x<=y for x,y in zip(sorted, sorted[1:]))
Upravený zdrojový text testů:
"""Test naivního algoritmu bublinkového řazení.""" from hypothesis import given from hypothesis.strategies import lists, integers from bubble_sort import bubble_sort @given(lists(integers(), min_size=5)) def test_bubble_sort(alist): sorted = bubble_sort(alist) assert all(x<=y for x,y in zip(sorted, sorted[1:]))
Výsledek běhu takto upravených testů:
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_6/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/bubble_sort_6 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item bubble_sort_test.py::test_bubble_sort FAILED [100%] =================================== FAILURES =================================== _______________________________ test_bubble_sort _______________________________ @given(lists(integers(), min_size=5)) > def test_bubble_sort(alist): bubble_sort_test.py:10: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ alist = [0, -1, 0, 0, 0] @given(lists(integers(), min_size=5)) def test_bubble_sort(alist): sorted = bubble_sort(alist) > assert all(x<=y for x,y in zip(sorted, sorted[1:])) E assert False E + where False = all(<generator object test_bubble_sort.<locals>.<genexpr> at 0x7f45ddda0fc0>) bubble_sort_test.py:12: AssertionError ---------------------------------- Hypothesis ---------------------------------- Falsifying example: test_bubble_sort( alist=[0, 0, 0, 0, -1], ) ============================ Hypothesis Statistics ============================= bubble_sort_test.py::test_bubble_sort: - during generate phase (0.06 seconds): - Typical runtimes: 0-22 ms, ~ 71% in data generation - 12 passing examples, 6 failing examples, 0 invalid examples - Found 1 failing example in this phase - during shrink phase (0.13 seconds): - Typical runtimes: 0-1 ms, ~ 83% in data generation - 6 passing examples, 14 failing examples, 59 invalid examples - Tried 79 shrinks of which 18 were successful - Stopped because nothing left to do =========================== short test summary info ============================ FAILED bubble_sort_test.py::test_bubble_sort - assert False ============================== 1 failed in 0.22s ===============================
12. Jednoduchá šifra typu ROT13
V navazujících kapitolách budeme testovat jiný algoritmus, opět velmi jednoduchý. Bude se jednat o jednu z možných implementací šifry ROT13, v níž jsou písmena z ASCII posunuta o 13 pozic nahoru (s tím, že se používá „modulo aritmetika“). V Pythonu je implementace této šifry snadná a je založena na funkci maketrans:
"""Jednoduchá šifra typu ROT13.""" import string def rot13(text): """Jednoduchá šifra typu ROT13.""" r = str.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") return text.translate(r) if __name__ == '__main__': sentence = "Příliš žluťoučký kůň úpěl ďábelské ódy." print(rot13(sentence)) print(rot13(rot13(sentence)))
Ve výpisu si můžeme ověřit, že dvojím zašifrováním získáme původní vstup a taktéž to, že znaky mimo základní ASCII nejsou změněny:
Cříyvš žyhťbhčxý xůň úcěy ďáoryfxé óql. Příliš žluťoučký kůň úpěl ďábelské ódy.
Help on built-in function maketrans: maketrans(x, y=None, z=None, /) Return a translation table usable for str.translate(). If there is only one argument, it must be a dictionary mapping Unicode ordinals (integers) or characters to Unicode ordinals, strings or None. Character keys will be then converted to ordinals. If there are two arguments, they must be strings of equal length, and in the resulting dictionary, each character in x will be mapped to the character at the same position in y. If there is a third argument, it must be a string, whose characters will be mapped to None in the result.
13. Otestování jednoduché šifry
Pro otestování této šifry můžeme použít základní vlastnost šifry ROT13 – dvojí aplikace této šifry by měla vrátit původní text (to je důvod, proč se používá posun o 13 pozic, protože v ASCII je jen 26 písmen ve dvou velikostech):
"""Test jednoduché šifry typu ROT13.""" from hypothesis import given from hypothesis.strategies import text from rot13 import rot13 @given(text()) def test_rot13(s): """Test jednoduché šifry typu ROT13.""" assert rot13(rot13(s)) == s
Test by měl projít bez chyby:
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_1/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_1 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item rot13_test.py::test_rot13 PASSED [100%] ============================ Hypothesis Statistics ============================= rot13_test.py::test_rot13: - during generate phase (0.16 seconds): - Typical runtimes: 0-1 ms, ~ 71% in data generation - 100 passing examples, 0 failing examples, 6 invalid examples - Stopped because settings.max_examples=100 ============================== 1 passed in 0.61s ===============================
14. Zanesení chyby do algoritmu šifrování
Do algoritmu šifry nyní zaneseme chybu, která se bude týkat mapování C na P. Namísto toho bude znak C mapován na mezeru:
"""Jednoduchá šifra typu ROT13.""" from hypothesis import given from hypothesis.strategies import text import string def rot13(text): """Jednoduchá šifra typu ROT13.""" r = str.maketrans( "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", "NO QRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm") return text.translate(r) if __name__ == '__main__': sentence = "Příliš žluťoučký kůň úpěl ďábelské ódy." print(rot13(sentence)) print(rot13(rot13(sentence)))
Tato chyba je ihned patrná při spuštění hlavního bloku (první písmeno dešifrované věty):
Cříyvš žyhťbhčxý xůň úcěy ďáoryfxé óql. říliš žluťoučký kůň úpěl ďábelské ódy.
Zajímavé ovšem je, že po spuštění testů se chyba nenalezne:
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_2/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_2 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item rot13_test.py::test_rot13 PASSED [100%] ============================ Hypothesis Statistics ============================= rot13_test.py::test_rot13: - during reuse phase (0.00 seconds): - Typical runtimes: < 1ms, ~ 72% in data generation - 1 passing examples, 0 failing examples, 0 invalid examples - during generate phase (1.41 seconds): - Typical runtimes: 0-2 ms, ~ 85% in data generation - 499 passing examples, 0 failing examples, 30 invalid examples - Stopped because settings.max_examples=500 ============================== 1 passed in 1.45s ===============================
15. Přesnější specifikace podoby generovaných textových dat
Důvodem, proč nebyla chyba nalezena, je fakt, že se Hypothesis snaží generovat co „nejpodivnější“ řetězce, ve kterých se používají různé znaky z celého Unicode. Z tohoto pohledu je znak C či P zcela normální a je jen velmi malá pravděpodobnost, že bude použit a chyba tak bude nalezena. Ovšem snadno můžeme zařídit, aby se vytvářely testovací řetězce z pouze omezené množiny znaků, zde konkrétně znaky s kódy 32 až 127:
"""Test jednoduché šifry typu ROT13.""" from hypothesis import given, note, settings from hypothesis.strategies import text, characters from rot13 import rot13 @given(text(characters(min_codepoint=32, max_codepoint=127), min_size=5)) @settings(max_examples=500) def test_rot13(s): """Test jednoduché šifry typu ROT13.""" assert rot13(rot13(s)) == s
Nyní již bude chyba v algoritmu nalezena, a to dokonce velmi rychle – v řádu desítek iterací (a nikoli tisícovek):
$ pytest -v --hypothesis-show-statistics ============================= test session starts ============================== platform linux -- Python 3.6.6, pytest-5.4.2, py-1.5.2, pluggy-0.13.1 -- /usr/bin/python3 cachedir: .pytest_cache hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_3/.hypothesis/examples') rootdir: /home/ptisnovs/src/python/testing-in-python/hypothesis/rot13_3 plugins: print-0.1.3, voluptuous-1.0.2, hypothesis-5.16.0, cov-2.5.1 collecting ... collected 1 item rot13_test.py::test_rot13 FAILED [100%] =================================== FAILURES =================================== __________________________________ test_rot13 __________________________________ @given(text(characters(min_codepoint=32, max_codepoint=127), min_size=5)) > @settings(max_examples=500) def test_rot13(s): rot13_test.py:10: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ s = '0000P' @given(text(characters(min_codepoint=32, max_codepoint=127), min_size=5)) @settings(max_examples=500) def test_rot13(s): """Test jednoduché šifry typu ROT13.""" > assert rot13(rot13(s)) == s E AssertionError: assert '0000 ' == '0000P' E - 0000P E ? ^ E + 0000 E ? ^ rot13_test.py:13: AssertionError ---------------------------------- Hypothesis ---------------------------------- Falsifying example: test_rot13( s='0000P', ) ============================ Hypothesis Statistics ============================= rot13_test.py::test_rot13: - during generate phase (0.04 seconds): - Typical runtimes: 0-22 ms, ~ 65% in data generation - 8 passing examples, 2 failing examples, 0 invalid examples - Found 1 failing example in this phase - during shrink phase (0.13 seconds): - Typical runtimes: 0-1 ms, ~ 77% in data generation - 11 passing examples, 18 failing examples, 48 invalid examples - Tried 77 shrinks of which 17 were successful - Stopped because nothing left to do =========================== short test summary info ============================ FAILED rot13_test.py::test_rot13 - AssertionError: assert '0000 ' == '0000P' ============================== 1 failed in 0.60s ===============================
16. Metody map a filter
Ve strategiích je možné použít i metody filter a map pro omezení množiny vstupních dat. Díky tomu lze například zařídit – i když poměrně neefektivně – aby se vytvářely seznamy s kladnými sudými čísly:
from hypothesis.strategies import lists, integers g = lists(integers().filter(lambda x: x > 0 and x % 2 == 0)) for _ in range(10): print(g.example())
Příklad výsledku (bude se lišit v každém spuštění):
[] [] [] [114, 297635958, 36382594596201776729035817479773596128] [] [] [1584, 20756, 27064] [] [] [6256, 16432]
Podobně lze omezit počet prvků v generovaných seznamech (což je ovšem velmi neefektivní způsob):
from hypothesis.strategies import lists, integers g = lists(integers().filter(lambda x: x > 0 and x % 2 == 0)).filter(lambda x: len(x) > 2 and len(x) < 6) for _ in range(10): print(g.example())
Opět si uveďme příklad výsledku (bude se lišit v každém spuštění):
[692, 25368, 17078, 8076] [32726, 187067303079521214, 30482] [32114, 31920, 120, 34, 4414] [26344, 49216631145512026322426062367560067482, 98, 32088, 2480] [8892, 14870, 2046] [110, 9412, 26768] [10434, 58, 7836, 12460] [141471345277595789266895116305060658262, 8302, 8470] [921905719452052336, 5382, 40] [7652, 638723812, 2104125796, 19320]
Funkce map dokáže zajistit, že na vstupu bude test dostávat setříděné seznamy:
from hypothesis.strategies import lists, integers g = lists(integers()).map(sorted) for _ in range(10): print(g.example())
S výsledky:
[0] [-20957] [0] [0] [0] [0] [] [0] [] [0]
A nakonec si všechny předchozí příklady můžeme zkombinovat – bude se generovat seznam se sudými kladnými čísly, jejichž počet se bude pohybovat od tří do pěti:
from hypothesis.strategies import lists, integers g = lists(integers().filter(lambda x: x > 0 and x % 2 == 0)).filter(lambda x: len(x) > 2 and len(x) < 6).map(sorted) for _ in range(10): print(g.example())
S výsledky:
[12, 6276, 20218, 639217126, 1354229336] [6, 52, 82, 165210043927242131602779831719279821612] [1634, 11134, 22630, 2119407794] [26, 2586, 14584, 28060] [62, 2606, 8938] [92, 10582, 886236136, 1731330112] [28, 46, 4008526909638794906] [4, 42, 112] [96, 108, 25038, 25970] [22, 126, 13974]
17. Kombinace více strategií
Strategie lze taktéž zkombinovat, a to s využitím přetíženého operátoru |. Příkladem může být generování hodnot, které mohou být typu None (tedy jediná hodnota None), typu Boolean (True či False), popř. celé číslo v rozsahu 0 až 1000:
from hypothesis.strategies import none, booleans, integers g = none() | booleans() | integers(min_value=0, max_value=1000) for _ in range(20): print(g.example())
Příklad výsledku:
True 244 None 339 42 False False None False 341 264 956 None 0 934 None True 999 False 522
18. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/testing-in-python. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady a jejich části, které naleznete v následující tabulce:
19. Předchozí články s tématem testování (nejenom) v Pythonu
Tématem testování jsme se již na stránkách Rootu několikrát zabývali. Jedná se mj. o následující články:
- Použití Pythonu pro tvorbu testů: od jednotkových testů až po testy UI
https://www.root.cz/clanky/pouziti-pythonu-pro-tvorbu-testu-od-jednotkovych-testu-az-po-testy-ui/ - Použití Pythonu pro tvorbu testů: použití třídy Mock z knihovny unittest.mock
https://www.root.cz/clanky/pouziti-pythonu-pro-tvorbu-testu-pouziti-tridy-mock-z-knihovny-unittest-mock/ - Použití nástroje pytest pro tvorbu jednotkových testů a benchmarků
https://www.root.cz/clanky/pouziti-nastroje-pytest-pro-tvorbu-jednotkovych-testu-a-benchmarku/ - Nástroj pytest a jednotkové testy: fixtures, výjimky, parametrizace testů
https://www.root.cz/clanky/nastroj-pytest-a-jednotkove-testy-fixtures-vyjimky-parametrizace-testu/ - Nástroj pytest a jednotkové testy: životní cyklus testů, užitečné tipy a triky
https://www.root.cz/clanky/nastroj-pytest-a-jednotkove-testy-zivotni-cyklus-testu-uzitecne-tipy-a-triky/ - Struktura projektů s jednotkovými testy, využití Travis CI
https://www.root.cz/clanky/struktura-projektu-s-jednotkovymi-testy-vyuziti-travis-ci/ - Omezení stavového prostoru testovaných funkcí a metod
https://www.root.cz/clanky/omezeni-stavoveho-prostoru-testovanych-funkci-a-metod/ - Behavior-driven development v Pythonu s využitím knihovny Behave
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave/ - Behavior-driven development v Pythonu s využitím knihovny Behave (druhá část)
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-druha-cast/ - Behavior-driven development v Pythonu s využitím knihovny Behave (závěrečná část)
https://www.root.cz/clanky/behavior-driven-development-v-pythonu-s-vyuzitim-knihovny-behave-zaverecna-cast/ - Validace datových struktur v Pythonu pomocí knihoven Schemagic a Schema
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-pomoci-knihoven-schemagic-a-schema/ - Validace datových struktur v Pythonu (2. část)
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-2-cast/ - Validace datových struktur v Pythonu (dokončení)
https://www.root.cz/clanky/validace-datovych-struktur-v-pythonu-dokonceni/ - Univerzální testovací nástroj Robot Framework
https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework/ - Univerzální testovací nástroj Robot Framework a BDD testy
https://www.root.cz/clanky/univerzalni-testovaci-nastroj-robot-framework-a-bdd-testy/ - Úvod do problematiky fuzzingu a fuzz testování
https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani/ - Úvod do problematiky fuzzingu a fuzz testování – složení vlastního fuzzeru
https://www.root.cz/clanky/uvod-do-problematiky-fuzzingu-a-fuzz-testovani-slozeni-vlastniho-fuzzeru/ - Knihovny a moduly usnadňující testování aplikací naprogramovaných v jazyce Clojure
https://www.root.cz/clanky/knihovny-a-moduly-usnadnujici-testovani-aplikaci-naprogramovanych-v-jazyce-clojure/ - Validace dat s využitím knihovny spec v Clojure 1.9.0
https://www.root.cz/clanky/validace-dat-s-vyuzitim-knihovny-spec-v-clojure-1–9–0/ - Testování aplikací naprogramovaných v jazyce Go
https://www.root.cz/clanky/testovani-aplikaci-naprogramovanych-v-jazyce-go/ - Knihovny určené pro tvorbu testů v programovacím jazyce Go
https://www.root.cz/clanky/knihovny-urcene-pro-tvorbu-testu-v-programovacim-jazyce-go/ - Testování aplikací psaných v Go s využitím knihoven Goblin a Frisby
https://www.root.cz/clanky/testovani-aplikaci-psanych-v-go-s-vyuzitim-knihoven-goblin-a-frisby/ - Testování Go aplikací s využitím knihovny GΩmega a frameworku Ginkgo
https://www.root.cz/clanky/testovani-go-aplikaci-s-vyuzitim-knihovny-gomega-mega-a-frameworku-ginkgo/ - Tvorba BDD testů s využitím jazyka Go a nástroje godog
https://www.root.cz/clanky/tvorba-bdd-testu-s-vyuzitim-jazyka-go-a-nastroje-godog/ - Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem/ - Použití Go pro automatizaci práce s aplikacemi s interaktivním příkazovým řádkem (dokončení)
https://www.root.cz/clanky/pouziti-go-pro-automatizaci-prace-s-aplikacemi-s-interaktivnim-prikazovym-radkem-dokonceni/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure/ - Použití jazyka Gherkin při tvorbě testovacích scénářů pro aplikace psané v Clojure (2)
https://www.root.cz/clanky/pouziti-jazyka-gherkin-pri-tvorbe-testovacich-scenaru-pro-aplikace-psane-v-nbsp-clojure-2/
20. Odkazy na Internetu
- Hoare logic
https://en.wikipedia.org/wiki/Hoare_logic - Goto Fail, Heartbleed, and Unit Testing Culture
https://martinfowler.com/articles/testing-culture.html - PEP-484
https://www.python.org/dev/peps/pep-0484/ - In-depth: Functional programming in C++
https://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php - mypy
http://www.mypy-lang.org/ - Welcome to Mypy documentation!
https://mypy.readthedocs.io/en/latest/index.html - mypy na GitHubu
https://github.com/python/mypy - mypy 0.770 na PyPi
https://pypi.org/project/mypy/ - Extensions for mypy (separated out from mypy/extensions)
https://github.com/python/mypy_extensions - The Mypy Blog
https://mypy-lang.blogspot.com/2020/03/mypy-0770-released.html - Our journey to type checking 4 million lines of Python
https://dropbox.tech/application/our-journey-to-type-checking-4-million-lines-of-python - Type-Checking Python Programs With Type Hints and mypy
https://www.youtube.com/watch?v=2×WhaALHTvU - Refactoring to Immutability – Kevlin Henney
https://www.youtube.com/watch?v=APUCMSPiNh4 - Bernat Gabor – Type hinting (and mypy) – PyCon 2019
https://www.youtube.com/watch?v=hTrjTAPnA_k - Stanford Seminar – Optional Static Typing for Python
https://www.youtube.com/watch?v=GiZKuyLKvAA - mypy Getting to Four Million Lines of Typed Python – Michael Sullivan
https://www.youtube.com/watch?v=FT_WHV4-QcU - Shebang
https://en.wikipedia.org/wiki/Shebang_(Unix) - pytest 5.4.2 na PyPi
https://pypi.org/project/pytest/ - Hillel Wayne – Beyond Unit Tests: Taking Your Testing to the Next Level – PyCon 2018
https://www.youtube.com/watch?v=MYucYon2-lk - Awesome Python – testing
https://github.com/vinta/awesome-python#testing - pytest Plugins Compatibility
http://plugincompat.herokuapp.com/ - Selenium (pro Python)
https://pypi.org/project/selenium/ - Getting Started With Testing in Python
https://realpython.com/python-testing/ - unittest.mock — mock object library
https://docs.python.org/3.5/library/unittest.mock.html - mock 2.0.0
https://pypi.python.org/pypi/mock - An Introduction to Mocking in Python
https://www.toptal.com/python/an-introduction-to-mocking-in-python - Mock – Mocking and Testing Library
http://mock.readthedocs.io/en/stable/ - Python Mocking 101: Fake It Before You Make It
https://blog.fugue.co/2016–02–11-python-mocking-101.html - Nauč se Python! – Testování
http://naucse.python.cz/lessons/intro/testing/ - Flexmock (dokumentace)
https://flexmock.readthedocs.io/en/latest/ - Test Fixture (Wikipedia)
https://en.wikipedia.org/wiki/Test_fixture - Mock object (Wikipedia)
https://en.wikipedia.org/wiki/Mock_object - Extrémní programování
https://cs.wikipedia.org/wiki/Extr%C3%A9mn%C3%AD_programov%C3%A1n%C3%AD - Programování řízené testy
https://cs.wikipedia.org/wiki/Programov%C3%A1n%C3%AD_%C5%99%C3%ADzen%C3%A9_testy - Pip (dokumentace)
https://pip.pypa.io/en/stable/ - Tox
https://tox.readthedocs.io/en/latest/ - pytest: helps you write better programs
https://docs.pytest.org/en/latest/ - doctest — Test interactive Python examples
https://docs.python.org/dev/library/doctest.html#module-doctest - unittest — Unit testing framework
https://docs.python.org/dev/library/unittest.html - Python namespaces
https://bytebaker.com/2008/07/30/python-namespaces/ - Namespaces and Scopes
https://www.python-course.eu/namespaces.php - Stránka projektu Robot Framework
https://robotframework.org/ - GitHub repositář Robot Frameworku
https://github.com/robotframework/robotframework - Robot Framework (Wikipedia)
https://en.wikipedia.org/wiki/Robot_Framework - Tutoriál Robot Frameworku
http://www.robotframeworktutorial.com/ - Robot Framework Documentation
https://robotframework.org/robotframework/ - Robot Framework Introduction
https://blog.testproject.io/2016/11/22/robot-framework-introduction/ - robotframework 3.1.2 na PyPi
https://pypi.org/project/robotframework/ - Robot Framework demo (GitHub)
https://github.com/robotframework/RobotDemo - Robot Framework web testing demo using SeleniumLibrary
https://github.com/robotframework/WebDemo - Robot Framework for Mobile Test Automation Demo
https://www.youtube.com/watch?v=06LsU08slP8 - Gherkin
https://cucumber.io/docs/gherkin/ - Selenium
https://selenium.dev/ - SeleniumLibrary
https://robotframework.org/ - The Practical Test Pyramid
https://martinfowler.com/articles/practical-test-pyramid.html - Acceptance Tests and the Testing Pyramid
http://www.blog.acceptancetestdrivendevelopment.com/acceptance-tests-and-the-testing-pyramid/ - Tab-separated values
https://en.wikipedia.org/wiki/Tab-separated_values - A quick guide about Python implementations
https://blog.rmotr.com/a-quick-guide-about-python-implementations-aa224109f321 - radamsa
https://gitlab.com/akihe/radamsa - Fuzzing (Wikipedia)
https://en.wikipedia.org/wiki/Fuzzing - american fuzzy lop
http://lcamtuf.coredump.cx/afl/ - Fuzzing: the new unit testing
https://go-talks.appspot.com/github.com/dvyukov/go-fuzz/slides/fuzzing.slide#1 - Corpus for github.com/dvyukov/go-fuzz examples
https://github.com/dvyukov/go-fuzz-corpus - AFL – QuickStartGuide.txt
https://github.com/google/AFL/blob/master/docs/QuickStartGuide.txt - Introduction to Fuzzing in Python with AFL
https://alexgaynor.net/2015/apr/13/introduction-to-fuzzing-in-python-with-afl/ - Writing a Simple Fuzzer in Python
https://jmcph4.github.io/2018/01/19/writing-a-simple-fuzzer-in-python/ - How to Fuzz Go Code with go-fuzz (Continuously)
https://fuzzit.dev/2019/10/02/how-to-fuzz-go-code-with-go-fuzz-continuously/ - Golang Fuzzing: A go-fuzz Tutorial and Example
http://networkbit.ch/golang-fuzzing/ - Fuzzing Python Modules
https://stackoverflow.com/questions/20749026/fuzzing-python-modules - 0×3 Python Tutorial: Fuzzer
http://www.primalsecurity.net/0×3-python-tutorial-fuzzer/ - fuzzing na PyPi
https://pypi.org/project/fuzzing/ - Fuzzing 0.3.2 documentation
https://fuzzing.readthedocs.io/en/latest/ - Randomized testing for Go
https://github.com/dvyukov/go-fuzz - HTTP/2 fuzzer written in Golang
https://github.com/c0nrad/http2fuzz - Ffuf (Fuzz Faster U Fool) – An Open Source Fast Web Fuzzing Tool
https://hacknews.co/hacking-tools/20191208/ffuf-fuzz-faster-u-fool-an-open-source-fast-web-fuzzing-tool.html - Continuous Fuzzing Made Simple
https://fuzzit.dev/ - Halt and Catch Fire
https://en.wikipedia.org/wiki/Halt_and_Catch_Fire#Intel_x86 - Random testing
https://en.wikipedia.org/wiki/Random_testing - Monkey testing
https://en.wikipedia.org/wiki/Monkey_testing - Fuzzing for Software Security Testing and Quality Assurance, Second Edition
https://books.google.at/books?id=tKN5DwAAQBAJ&pg=PR15&lpg=PR15&q=%22I+settled+on+the+term+fuzz%22&redir_esc=y&hl=de#v=onepage&q=%22I%20settled%20on%20the%20term%20fuzz%22&f=false - libFuzzer – a library for coverage-guided fuzz testing
https://llvm.org/docs/LibFuzzer.html - fuzzy-swagger na PyPi
https://pypi.org/project/fuzzy-swagger/ - fuzzy-swagger na GitHubu
https://github.com/namuan/fuzzy-swagger - Fuzz testing tools for Python
https://wiki.python.org/moin/PythonTestingToolsTaxonomy#Fuzz_Testing_Tools - A curated list of awesome Go frameworks, libraries and software
https://github.com/avelino/awesome-go - gofuzz: a library for populating go objects with random values
https://github.com/google/gofuzz - tavor: A generic fuzzing and delta-debugging framework
https://github.com/zimmski/tavor - hypothesis na GitHubu
https://github.com/HypothesisWorks/hypothesis - Hypothesis: Test faster, fix more
https://hypothesis.works/ - Hypothesis
https://hypothesis.works/articles/intro/ - What is Hypothesis?
https://hypothesis.works/articles/what-is-hypothesis/ - What is Property Based Testing?
https://hypothesis.works/articles/what-is-property-based-testing/ - Databáze CVE
https://www.cvedetails.com/ - Fuzz test Python modules with libFuzzer
https://github.com/eerimoq/pyfuzzer - Taof – The art of fuzzing
https://sourceforge.net/projects/taof/ - JQF + Zest: Coverage-guided semantic fuzzing for Java
https://github.com/rohanpadhye/jqf - http2fuzz
https://github.com/c0nrad/http2fuzz - Demystifying hypothesis testing with simple Python examples
https://towardsdatascience.com/demystifying-hypothesis-testing-with-simple-python-examples-4997ad3c5294 - Testování
http://voho.eu/wiki/testovani/ - Unit testing (Wikipedia.en)
https://en.wikipedia.org/wiki/Unit_testing - Unit testing (Wikipedia.cz)
https://cs.wikipedia.org/wiki/Unit_testing - Unit Test vs Integration Test
https://www.youtube.com/watch?v=0GypdsJulKE - TestDouble
https://martinfowler.com/bliki/TestDouble.html - Test Double
http://xunitpatterns.com/Test%20Double.html - Test-driven development (Wikipedia)
https://en.wikipedia.org/wiki/Test-driven_development - Acceptance test–driven development
https://en.wikipedia.org/wiki/Acceptance_test%E2%80%93driven_development - Gauge
https://gauge.org/ - Gauge (software)
https://en.wikipedia.org/wiki/Gauge_(software) - PYPL PopularitY of Programming Language
https://pypl.github.io/PYPL.html - Testing is Good. Pyramids are Bad. Ice Cream Cones are the Worst
https://medium.com/@fistsOfReason/testing-is-good-pyramids-are-bad-ice-cream-cones-are-the-worst-ad94b9b2f05f - Články a zprávičky věnující se Pythonu
https://www.root.cz/n/python/ - PythonTestingToolsTaxonomy
https://wiki.python.org/moin/PythonTestingToolsTaxonomy - Top 6 BEST Python Testing Frameworks [Updated 2020 List]
https://www.softwaretestinghelp.com/python-testing-frameworks/ - pytest-print 0.1.3
https://pypi.org/project/pytest-print/ - pytest fixtures: explicit, modular, scalable
https://docs.pytest.org/en/latest/fixture.html - PyTest Tutorial: What is, Install, Fixture, Assertions
https://www.guru99.com/pytest-tutorial.html - Pytest – Fixtures
https://www.tutorialspoint.com/pytest/pytest_fixtures.htm - Marking test functions with attributes
https://docs.pytest.org/en/latest/mark.html - pytest-print
https://pytest-print.readthedocs.io/en/latest/ - Continuous integration
https://en.wikipedia.org/wiki/Continuous_integration - Travis CI
https://travis-ci.org/ - Mutation testing
https://en.wikipedia.org/wiki/Mutation_testing - Články o Hypothesis
https://news.ycombinator.com/from?site=hypothesis.works - Testovací případ
https://cs.wikipedia.org/wiki/Testovac%C3%AD_p%C5%99%C3%ADpad - Most testing is ineffective
https://hypothesis.works/