1. diel - Úvod do testovania softvéru v Pythone
Vitajte, všetci pokročilí programátori, u prvého tutoriálu online kurzu testovania aplikácií v Pythone. Kurz je tvorený najmä na základe praktických skúseností s projektmi väčšieho rozsahu. Naučíme sa v ňom postupne pokrývať kód rôznymi typmi testov a tým vytvárať spoľahlivé a kvalitné aplikácie. Je určený pre všetkých, ktorí sa usilujú o slušné zamestnanie s dobrým platovým ohodnotením. Nabité znalosti využijú aj tí, ktorí majú svoju aplikáciu a radi by ju ďalej pohodlne rozširovali tak, aby sa nemuseli báť, že zmeny kódu rozbijú pôvodnú funkčnosť:
-
import unittest class TagDAOTest(unittest.TestCase): known_tags = None @classmethod def setUpClass(cls): TestEnvironment.init() cls.known_tags = TestEnvironment.get_data(Tag) cls.assertGreater(len(cls.known_tags), 0) def setUp(self): self.dao = TagDAO()
Minimálne požiadavky
Pre úspešné absolvovanie kurzu je nutná znalosť jazyka Python aspoň v rozsahu našich kurzov:
Výhodou sú ďalej znalosti z kurzu:
Testovanie je vlastne takým štvrtým bodom osnovy vyššie, ktorý by mal každý dobrý programátor poznať, aby jeho práca mala skutočnú hodnotu.
Kedy testovať
Testovanie sa určite radí medzi dobré praktiky vývoja softvéru (best practices), ktorými sa tiež zaoberáme v samostatnom kurze Best practices pre návrh softvéru. Ďalšou takou praktikou je napr. používať viacvrstvovú architektúru a podobne. Rozdelenie logiky aplikácie do objektov a vrstiev znamená pre skúseného vývojára minimálne zdržanie a minimalizuje náklady na rozširovanie a udržiavanie neprehľadnej a nerozvrstvenej aplikácie. Na druhú stranu, niektoré praktiky vývoja softvéru by sme naopak nemali dodržiavať až takto extrémne a medzi ne patrí práve testovanie.
Hneď na úvod je potrebné spomenúť, že testovanie je veľmi dôležité a v určitej časti projektu dokonca nenahraditeľné. Na druhú stranu, v prvých fázach projektu (a to najmä pri start-upoch), kedy sa hrá na čas, funkčnosti aplikácie sa často menia, a treba ju čo najskôr spustiť, nie je vôbec dobrý nápad testy písať. Museli by sa často meniť, zbytočne by zdržiavali a mohli by poškodiť rozjazd.
Niektorým z nás teraz možno bleskla hlavou teórie okolo TDD (Test-Driven Development), ktorá sa naopak opiera o neustále testovanie úplne všetkého. Ako teoretický koncept znie síce pekne, ale v praxi by mal dobrý programátor dokázať myslieť aj trochu ako manažér a nakladať rozumne s vývojovým budgetom (rozpočtom). Koniec koncov, aj jeho plat vychádza z toho, ako je aplikácia konkurencieschopná. Pokiaľ máme naopak aplikáciu, ktorá má už niekoľko stabilných funkcií a chceme ju ďalej rozširovať, bez testov sa nezaobídeme.
Testy teda pokrývame také aplikácie, ktoré už majú niekoľko stabilných funkčností.
Rozširovanie softvéru
Akékoľvek rozširovanie softvéru, pokiaľ ho robíme kvalitne, vždy znamená zmenu existujúcich funkčností. Do istej miery môže kvalitná analýza a návrh pripraviť pôdu pre budúce úpravy, ale aj keď sa budeme veľa snažiť, trh sa mení nekontrolovane a úspešnú aplikáciu bude treba upravovať zo všetkých strán. Ak existujúce funkcie nebudeme upravovať, začne nám vznikať redundantný kód Napríklad napíšeme podobnú metódu znova, namiesto toho, aby sme len parametrizovali nejakú, ktorú už aplikácia má. Alebo pridáme zbytočne ďalšie databázové tabuľky namiesto toho, aby sme len upravili dátový model a podobne.
Asi vieme, že vyhýbať sa úprave existujúceho kódu aplikácie sa vôbec neoplatí. Trpel by tým jej návrh a do budúcnosti by bolo veľmi ťažké taký zlepenec nejako upravovať alebo rozširovať. Zmeny by sme potom museli vykonávať na niekoľkých miestach, bloky kódu by sa opakovali a podobne. Došli sme teda k tomu, že svoju aplikáciu budeme stále meniť a prepisovať, takto vývoj softvéru jednoducho vyzerá. A kto potom otestuje, že všetko funguje? Človek? Keď sme došli k tomu, že musíme testovať, či sa predchádzajúca funkčnosť novou úpravou nerozbila, povedzme si tiež, prečo nemôže testovanie vykonávať vždy len človek.
Prečo testovanie nemôže robiť človek?
Zamyslime sa nad naivnou predstavou.
Očakávania
Programátor alebo tester si sadne k počítaču a preklikne jednu službu za druhú, či fungujú. Zoberme si napríklad ICTdemy. Tester by sa skúsil zaregistrovať, prihlásiť sa, zmeniť si zabudnuté heslo, upraviť profil, dobiť body platobnou kartou a tak ďalej. Funkčnosťou (use caseov) sú v tunajšom systéme stovky. Možno nás napadá, že človek by ich testoval hodiny a preto testovanie necháme urobiť stroj, ktorý to urobí pravdepodobne za pár minút. Áno, ale stále ešte nemáme úplne pravdu. Sadnúť si a deň klikať nie je v zásade stále taký problém, testy sa píšu dlho, možno by sa neautomatizované testovanie ešte aj vyplatilo. Ale...
Realita
Predstavme si však, že takto testujeme aplikáciu, sme niekde v polovici testovacieho scenára a nájdeme chybu. Nejde napísať komentár k článku napr. kvôli zmene nejakého validátora. Chybu opravíme, úspešne pokračujeme až do konca scenára. Otestovanú aplikáciu nasadíme na produkčný server a ďalší deň nám príde e-mail, že nejdú kupovať články. Po chvíli skúmania zistíme, že oprava, ktorú sme vykonali, rozbila inú funkčnosť, ktorú sme už otestovali predtým. Takto sme mohli prísť aj o niekoľko tisíc eur, kým nám chybu niekto nahlásil. A to sme vôbec nespomenuli katastrofické scenáre, kedy by došlo k nejakému úniku dát, zaspamovaniu používateľov a podobne.
Ak sa počas testovania vykoná oprava, musíme celý scenár vykonať od začiatku!
A že sa tých chýb obvykle nájde hneď niekoľko, celé testovanie sa pretiahne až na niekoľko dní klikania. Programátor toto pravdepodobne nevydrží a nevykoná testy starostlivo. Osobne som frustráciu z vykonávania tých istých úkonov stále dookola nikdy nezniesol Potom hrozí zanesenie chyby na produkciu.
A práve preto musí testy vykonávať stroj, ktorý celú aplikáciu prekliká obvykle maximálne za desiatky minút a môže testovanie robiť znova a znova a znova. Preto píšeme automatické testy. Toto vám bohužiaľ väčšina návodov na internete nepovie.
Typy testov
Zamerajme sa teraz na to, čo na aplikácii testujeme. Typov testov je hneď niekoľko, obvykle nepokrývajú úplne všetky možné scenáre (všetok kód), ale hovoríme o percentuálnom pokrytí testami (code coverage), väčšinou kritických častí aplikácie. Čím väčšia aplikácia je, tým viac typov testov potrebuje a tým viac funkčnosti obvykle pokrývame. Prvá verzia menších aplikácií väčšinou ešte nepotrebujú žiadne testy alebo len tie úplne základné, napr. aby sa do nich dalo registrovať.
Popíšme si tie najzákladnejšie typy testov.
Jednotkové testy (Unit testy)
Obvykle testujú univerzálne knižnice, nepíšeme ich pre kód špecifický pre danú aplikáciu. Jednotkové testy testujú triedy, presnejšie ich metódy, jednu po druhej. Odovzdávajú im rôzne vstupy a skúšajú, či sú ich výstupy korektné.
Nemá úplne zmysel pomocou unit testov testovať, či metóda obsahujúca databázový dotaz, ktorá je použitá v jednom kontroléri (alebo nejakej inej riadiacej vrstve), vracia čo má. Naopak dáva veľmi dobrý zmysel testovať napr. validátor telefónnych čísel, ktorý používame na 20 miestach alebo dokonca v niekoľkých aplikáciách. Takýto jednotkový test vyskúša napr. 20 rôznych správnych a nesprávnych telefónnych čísel a overí, či validátor naozaj spozná, ktoré sú validné a ktoré nie. Unit testy sú tzv. whitebox testy, to znamená, že ich píšeme s tým, že vieme, ako testovaný kód vo vnútri funguje (vidíme dovnútra).
Akceptačné testy
Tento typ testov je naopak úplne odtienený od toho, ako je aplikácia vo vnútri naprogramovaná, sú to teda blackbox testy (nevidíme dovnútra). Každý test obvykle testuje určitú funkčnosť, napr. test na písanie článkov by testoval jednotlivé use cases s tým spojené (odovzdať článok na schválenie, schváliť článok, zamietnu článok, publikovať článok ako administrátor...). Tieto testy obvykle využívajú framework Selenium, ktorý umožňuje automaticky klikať vo webovom prehliadači a simulovať internetového užívateľa, čo vašu aplikáciu používa. Týmito testami sa teda v podstate skúša špecifická logika aplikácie (databázové dotazy a podobne), testuje sa výsledok, ktorý aplikácia vygeneruje, nie priamo jej vnútorný kód.
Integračné testy
V dnešnej dobe dosahujú aplikácie už pomerne vysokú komplexnosť a veľmi často bývajú rozdelené do niekoľkých služieb, ktoré spolu komunikujú a sú vyvíjané zvlášť. Práve integračné testy dohliadajú na to, aby do seba všetko správne zapadalo. Niekedy to budú dva moduly (balíčky) jedného systému, ktoré si medzi sebou vymieňajú správy, inokedy dva čiastkové softvéry. A ešte inokedy môžeme testovať integrovanie so softvérom cudzej strany, teda vyvíjaným inou firmou.
Systémové testy
Aj keď aplikácia funguje dobre, na produkčnom prostredí podlieha ďalším vplyvom, s ktorými musíme tiež počítať, napr. že by mala zvládať obsluhovať tisíc užívateľov v jeden okamih. To by sme urobili záťažovým testom, ktorý patrí medzi systémové testy.
V-model
Úvod do problematiky softvérového testovania zakončime predstavením tzv. V-modelu. Ten rozširuje známy vodopádový model vývoja softvéru, ktorý má nasledujúce fázy:
- Analýza požiadaviek.
- High-level návrh.
- Detailné špecifikácie.
- Implementácia.
Ako iste vieme, celý softvér sa už dávno nevyvíja postupným vykonaním týchto štyroch fáz, ale iteračne, teda vykonávaním týchto fáz v krátkych časových intervaloch pre ďalšie a ďalšie časti aplikácie. V-model každej z týchto fáz priraďuje testovacia fáza (typ testov, o ktorých sme hovorili vyššie):
Vidíme, že názov v-modele je odvodený od podoby s písmenom V. Po implementácii pomocou unit testov overíme detailnú špecifikáciu, integračnými testami high-level návrh a akceptačnými testami či fungujú všetky use cases. V-model sa niekedy robí ešte o niekoľko poschodí vyššou, pokiaľ je aplikácia naozaj rozsiahla a vyžaduje viac typov testov, napr. ešte testy systémové.
V nasledujúcej lekcii, Testovanie v Pythone - Prvý unit test s knižnicou unittest, sa naučíme programovať unit testy, čo sú najjednoduchšie testy overujúce funkčnosť vnútorných knižníc (jednotiek), s ktorými sa obvykle začína pri pokrývaní aplikácie testami.