Business & Integration IT konzultant
Unit testing a najčastejšie chyby pri jednotkovom testovaní
Unit testovanie definícia (unit test definition)
Unit testing je typ testovania softvéru, ktorý sa zameriava na jednotlivé malé časti kódu alebo komponenty softvérového systému. Cieľom unit testovania je overiť, či každá malá časť – jednotka softvéru funguje tak, ako má a či spĺňa požiadavky. Unit testovanie zvyčajne vykonávajú developeri a vykonáva sa na začiatku vývojového procesu pred tým, ako sa kód integruje a testuje sa ako celok.
Unit testy sú automatizované a vykonávajú sa pri každej zmene kódu, aby sa zabezpečilo, že nový kód neporuší existujúcu funkčnosť. Unit testy sú navrhnuté tak, aby overovali najmenšiu možnú jednotku kódu, napríklad funkciu alebo metódu a testovali ju izolovane od zvyšku systému. To umožňuje vývojárom rýchlo identifikovať a opraviť prípadné problémy už na začiatku procesu vývoja, čím sa zlepšuje celková kvalita softvéru a skracuje sa čas potrebný na neskoršie testovanie.
Unit test vs integration test
Pri diskusii o unit testovaní je nevyhnutné rozlišovať medzi unit testami a integračnými testami. Zatiaľ čo unit testy sa zameriavajú na testovanie jednotlivých komponentov alebo jednotiek kódu izolovane, integračné testy overujú interakcie medzi týmito jednotkami a zabezpečujú, aby fungovali spoločne podľa očakávania.
V STLC (SDLC) alebo V modeli je unit testovanie prvou úrovňou testovania vykonávanou pred integračným testovaním.
Výhody unit testovania (unit test benefits)
- Unit testovanie umožňuje programátorovi vylepšiť kód a uistiť sa, že komponent funguje správne.
- Umožňuje testovať časti projektu bez čakania na dokončenie ostatných.
- Umožňuje vývojárom odhaliť a opraviť problémy v skorej fáze vývoja skôr, ako sa stanú väčšími a ťažšie opraviteľnými.
- Unit testovanie pomáha zabezpečiť, aby každá jednotka kódu fungovala tak, ako má a spĺňala požiadavky, čím sa zlepšuje celková kvalita softvéru.
- Umožňuje vývojárom pracovať rýchlejšie a efektívnejšie, pretože môžu overovať zmeny v kóde bez toho, aby museli čakať na testovanie celého systému.
- Poskytuje jasnú a stručnú dokumentáciu kódu a jeho správania, čo ostatným vývojárom uľahčuje pochopenie a údržbu softvéru.
- Unit testovanie umožňuje vývojárom bezpečne vykonávať zmeny v kóde, pretože si môžu overiť, že ich zmeny neporušia existujúcu funkčnosť.
- Môže znížiť čas a náklady potrebné na neskoršie testovanie, pretože pomáha identifikovať a opraviť problémy už v ranom štádiu vývoja.
Nevýhody unit testovania
- Proces je časovo náročný na písanie prípadov unit testovania.
- Unit testovanie nepokryje všetky chyby v module, pretože pri integračnom testovaní existuje možnosť výskytu chýb v moduloch.
- Nie je účinné na kontrolu chýb v časti používateľského rozhrania (UI) modulu.
- Vyžaduje si viac času na údržbu, keď sa zdrojový kód často mení.
- Úspech unit testovania závisí od vývojárov, ktorí musia napísať jasné, stručné a komplexné testovacie prípady na overenie kódu.
- Unit testovanie môže byť náročné, ak ide o komplexné jednotky, pretože môže byť ťažké izolovať a testovať jednotlivé jednotky oddelene od zvyšku systému.
Typy unit testovania (unit test types)
Existujú 2 typy unit testovania: manuálne a automatizované.
Manuálne unit testovanie predstavuje praktický prístup, pri ktorom testeri píšu a vykonávajú testovacie prípady bez pomoci nástrojov na automatizáciu alebo unit testovanie. Tento typ unit testovania je často flexibilnejší a v určitých súvislostiach môže byť výstižnejší. Vo všeobecnosti je však časovo náročnejší a náchylnejší na ľudské chyby.
Automatizované unit testovanie:
Vývojár napíše časť kódu v aplikácii len na otestovanie funkcie. Neskôr testovací kód zakomentuje a nakoniec odstráni, keď je aplikácia nasadená. Pomocou automatizačného frameworku vývojár programuje do testu kritériá na overenie správnosti kódu. Počas vykonávania testovacích prípadov framework zaznamenáva neúspešné testovacie prípady. Mnohé frameworky tieto neúspešné testovacie prípady aj automaticky označia a súhrnne nahlásia. V závislosti od závažnosti zlyhania môže framework zastaviť ďalšie testovanie.
Pracovný postup unit testovania je nasledovný: 1) Vytvorenie testovacích prípadov 2) Preskúmanie 3) Základné hodnotenie 4) Vykonanie testovacích prípadov
Metódy unit testov (unit test methods)
Existujú 3 typy metód unit testovania. Sú to:
Testovanie čiernej skrinky (Black Box Testing): Táto technika testovania sa používa pri testovaní komponentov pre vstupné, používateľské a výstupné časti.
Testovanie bielej skrinky (White Box Testing): Táto technika sa používa pri testovaní funkčného správania systému zadaním vstupu a kontrolou výstupu funkčnosti vrátane vnútornej štruktúry návrhu a kódu modulov.
Testovanie šedej skrinky (Gray Box Testing): Táto technika sa používa pri vykonávaní príslušných testovacích prípadov, testovacích metód a testovacích funkcií a pri analýze výkonu kódu modulov.
Anatómia unit testu
1. Test fixtures (testovacie predpoklady)
Testovacie predpoklady sú komponenty unit testu zodpovedné za prípravu prostredia potrebného na vykonanie testovacieho prípadu. Sú to predpoklady a nastavenia, ktoré potrebuješ na spustenie testovacích prípadov. Nazývajú sa aj „testovací kontext“ a vytvárajú počiatočné stavy pre testovanú jednotku, aby sa zabezpečilo jej kontrolovanejšie vykonávanie. Sú veľmi dôležité, pretože poskytujú konzistentné prostredie na opakovanie procesu testovania.
Povedzme napríklad, že máme blogovaciu aplikáciu a chceme otestovať modul vytvárania príspevkov. Testovacie predpoklady by mali obsahovať:
- Pripojenie k databáze príspevkov
- Vzorový príspevok s nadpismi, obsahom, informáciami o autorovi atď.
- Dočasné úložisko na spracovanie príloh príspevkov
- Testovacie konfiguračné nastavenia (predvolená viditeľnosť príspevku, možnosti formátovania atď.)
- Testovacie používateľské údaje
2. Testovací prípad
Unit testovací prípad je jednoducho časť kódu navrhnutá na overenie správania inej jednotky kódu, čím sa zabezpečí, že testovaná jednotka funguje podľa očakávania a prináša požadované výsledky. Tu je napríklad prípad unit testu (unit test example) funkcie, ktorá počíta súčet dvoch čísel a a b:
use PHPUnit\Framework\TestCase;
class MathTest extends TestCase {
public function testSum() {
// Premenné
$a = 5;
$b = 7;
$expectedResult = 12;
// Funkcia
$result = Math::sum($a, $b);
// Assertion - tvrdenie
$this->assertEquals($expectedResult, $result);
}
}
Tvrdenie použité v tomto kóde je $this->assertEquals($expectedResult, $result); overuje, že a + b sa skutočne rovná očakávanému výsledku 12.
3. Spúšťač testov
Test runner je framework na organizovanie vykonávania viacerých unit testov a tiež na poskytovanie hlásení a analýzu výsledkov testov. Dokáže prehľadávať kódovú bázu alebo adresáre, aby vyhľadal testovacie prípady a následne ich vykonal. Skvelé je, že test runner môže spúšťať testy podľa priority a zároveň spravovať testovacie prostredie a obsluhovať operácie nastavenia/odstavenia. Vďaka testovaciemu runneru môže byť testovaná jednotka izolovaná od externých závislostí.
4. Testovacie dáta
Testovacie dáta by sa mali starostlivo vyberať tak, aby pokrývali čo najviac scenárov danej jednotky, čím sa zabezpečí vysoké pokrytie testami. Vo všeobecnosti sa očakáva príprava dát pre:
- normálne prípady: typické a očakávané vstupné hodnoty pre danú jednotku
- hraničné prípady: vstupné hodnoty na hranici prijateľnej hranice
- neplatné/chybové prípady: neplatné vstupné hodnoty, aby sa zistilo, ako jednotka reaguje na chyby (chybovými správami alebo určitým správaním)
- edge prípady: vstupné hodnoty predstavujúce extrémne scenáre, ktoré majú významný vplyv na jednotku alebo systém
5. Mocking a Stubbing
Mocking a stubbing sú v podstate náhradami skutočných závislostí testovanej jednotky. Pri testovaní jednotiek sa vývojári musia sústrediť na izolované testovanie konkrétnej jednotky, ale v určitých scenároch budú na vykonanie testu potrebovať dve jednotky.
Napríklad môžeme mať triedu User, ktorá závisí od externej triedy EmailSender na odosielanie e-mailových oznámení. Trieda User má metódu sendWelcomeEmail(), ktorá zavolá EmailSender na odoslanie uvítacieho e-mailu novo zaregistrovanému používateľovi. Ak chceme metódu sendWelcomeEmail() otestovať izolovane bez skutočného odosielania e-mailov, môžeme vytvoriť maketový objekt triedy EmailSender. Vývojár sa potom nebude musieť starať o to, či externá jednotka (EmailSender) funguje dobre alebo nie. Testovaná jednotka je skutočne testovaná izolovane.
Nástroje na unit testovanie (unit test software tools)
Tu sú niektoré bežne používané nástroje na unit testovanie:
- Junit (for a unit test in java): Junit je bezplatný testovací nástroj používaný pre programovací jazyk Java. Poskytuje tvrdenia na identifikáciu testovacej metódy. Tento nástroj najprv otestuje údaje a potom ich vloží do časti kódu.
- NUnit: NUnit je široko používaný framework na unit testovanie, ktorý sa používa pre všetky jazyky .net. Je to nástroj s otvoreným zdrojovým kódom, ktorý umožňuje ručné písanie skriptov. Podporuje dátovo riadené testy, ktoré môžu bežať paralelne.
- JMockit: JMockit je open source nástroj na unit testovanie. Je to nástroj na pokrytie kódu s metrikami riadkov a ciest. Umožňuje mocking API so syntaxou záznamu a overovania. Tento nástroj ponúka pokrytie riadkov, pokrytie ciest a pokrytie dát.
- EMMA: EMMA je open-source súbor nástrojov na analýzu a vykazovanie kódu napísaného v jazyku Java. Emma podporuje typy pokrytia ako metóda, riadok, základný blok. Je založený na jazyku Java, takže je bez závislostí od externých knižníc a môže pristupovať k zdrojovému kódu.
- PHPUnit: PHPUnit je nástroj na unit testovanie pre programátorov PHP. Nástroj umožňuje vývojárom používať preddefinované metódy tvrdenia na potvrdenie, že sa systém správa určitým spôsobom.
- PyUnit (for a unit test in python), známy aj pod názvom Unittest, je framework na unit testovanie inšpirovaný JUnitom, ktorý je súčasťou inštalácií Pythonu. Poskytuje jednoduchý a rýchly spôsob spúšťania testovacích prípadov bez potreby ďalších inštalácií.
- MSTest / Visual Studio (for a unit test in c#): MSTest je framework na unit testovanie, ktorý je súčasťou Visual Studia, populárneho integrovaného vývojového prostredia (IDE) pre .NET. MSTest zjednodušuje proces unit testovania tým, že sa integruje priamo do prostredia Visual Studio IDE, čo je výhodné pre vývojárov využívajúcich Microsoft.
Ako vyzerajú unit testy?
Jednotka môže byť takmer čokoľvek, čo chceš – riadok kódu, metóda alebo trieda. Vo všeobecnosti však platí, že menšie je lepšie. Menšie testy ti poskytnú oveľa detailnejší pohľad na to, ako tvoj kód funguje. Je tu aj praktický aspekt, že keď testuješ veľmi malé jednotky, testy môžu byť spustené rýchlo, napríklad tisíc testov za sekundu.
Zoberme si túto ukážku kódu:
def divider (a, b)
return a/b
end
Pri použití jazyka Ruby by tieto malé testy mohli vyzerať takto:
class smallTest < MiniTest::Unit::testCase
def tiny_test
@a=9
@b=3
assert_equal(3, divider(a, b))
end
end
Tento príklad je príliš jednoduchý, ale dáva v predstavu o tom, čo myslím pod pojmom malý. Ak si chceš pozrieť priamy postup vytvárania unit testu, klikni sem unit test tutorial.
Životný priebeh unit testov (unit test life cycle)
Unit testy zvyčajne pozostávajú z troch fáz:
- Plánovanie – vývojári zvažujú, ktoré jednotky v kóde potrebujú otestovať a ako vykonať všetky relevantné funkcie každej jednotky, aby ju efektívne otestovali.
- Testovacie prípady a skripty – vývojári napíšu kód unit testov a pripravia skripty na vykonanie kódu.
- Unit testovanie a výsledky – nakoniec sa spustí unit test a vývojári môžu identifikovať chyby alebo problémy v kóde a opraviť ich.
Najlepšie postupy unit testovania (unit test best practices)
1. Napíš vhodné názvy testov
Základnou vecou, ktorú je potrebné zvážiť pri písaní testu, je výber názvu testu. Dobré názvy testov zlepšujú čitateľnosť kódu tak pre programátora, ako aj pre ostatných, ktorí môžu na tomto kóde v budúcnosti pracovať. Existujú štandardné konvencie pre pomenovanie (viac si prečítaj tu – unit test naming convention), ktoré je možné použiť pri unit testovaní.
2. Vytváraj jednoduché testy
Udržiavanie čo najjednoduchších kódov testov je kľúčom k zachovaniu ich správnosti. Unit testovacie kódy môžu mať aj chyby, najmä pri vysokej úrovni zložitosti.
3. Vytvor deterministické testy
Deterministický test dáva vždy rovnaký výsledok bez ohľadu na vstup, pokiaľ sa kód nemení. Tým sa minimalizuje výskyt falošne pozitívnych a falošne negatívnych výsledkov. Testy musia byť deterministické, pretože testu, ktorý prezentuje premenlivé výsledky, nemožno dôverovať.
4. Riešenie jedného prípadu použitia
Každý test by sa mal použiť na testovanie jedného prípadu použitia. Konkrétny testovací program by mal testovať jeden blok kódu. Tým sa overí výstup a získa sa lepší prehľad o príčine objavených chýb bez pochybností o tom, odkiaľ pochádzajú.
5. Snaž sa o maximálne pokrytie testov
Vývojári by mali kompletne a dôkladne otestovať softvérovú aplikáciu v čo najväčšom rozsahu. Nie vždy je to však uskutočniteľné vzhľadom na časové a finančné požiadavky. Napriek tomu sa vývojári musia snažiť vykonávať unit testy programu v čo najväčšej miere.
6. Navrhni unit testy tak, aby boli čo najrýchlejšie
Pomalé testy sú pre vývojárov zložité na vykonávanie. Spomaľujú proces a nedajú sa často používať. Pripúšťame, že rýchlosť testu je subjektívna a závisí od testovaného predmetu, ale každý test, ktorý trvá viac ako hodinu a 15 minút, možno klasifikovať ako pomalý.
7. Prijmi automatizáciu testov
Hoci unit testy možno vykonávať manuálne, súčasné postupy podporujú automatizovanú metódu testov. Ukázalo sa, že je nielen efektívnejšia a lacnejšia ale aj časovo úspornejšia.
Najčastejšie chyby pri unit testovaní a ako sa im vyhnúť
Neudržiavanie testov v aktuálnom stave
Testy vytvorené pred niekoľkými mesiacmi už nemusia platiť. Je veľmi dôležité vykonávať testy pravidelne, pretože zmeny v požiadavkách a kóde môžu spôsobiť problémy, ktoré nezodpovedajú tomu, čo by mali testovať. Zanedbanie aktualizácie testov môže viesť k falošným alebo zavádzajúcim výsledkom.
Riešenie: Pravidelné vykonávanie testov je nevyhnutné. Najlepšie pomocou automatizovaného unit testovania. Mal by si sa tiež uistiť, že sú v súlade s požiadavkami.
Množstvo testov
Jednou z hlavných chýb pri unit testovaní je nenájdenie správnej rovnováhy. Testovanie každého riadku kódu môže byť vyčerpávajúce a časovo náročné, zatiaľ čo pri testovaní príliš malého počtu častí môže dôjsť k prehliadnutiu kritických chýb. Je nevyhnutné zamerať sa na testovanie najdôležitejších funkcií a okrajových prípadov.
Riešenie: Na identifikáciu častí kódu, ktoré potrebujú najviac testovania, použi techniky, ako je analýza pokrytia kódu a hodnotenie rizík. Uprednostni testovanie zložitých funkcií, oblastí náchylných na chyby a funkcií zameraných na používateľa.
Testovanie zlých údajov
Použitie nesprávnych alebo nevhodných údajov na testovanie môže viesť k zavádzajúcim výsledkom. Takýmito príkladmi sú falošne pozitívne alebo negatívne výsledky. Napríklad vykonávanie testov s použitím nulových hodnôt by mohlo zakryť skutočné chyby alebo odlišné scenáre. Podobne vykonávanie testov s príliš podobnými a veľmi náhodnými údajmi by mohlo znížiť účinnosť a transparentnosť tvojich testovacích procesov.
Riešenie: Je nevyhnutné používať praktické a inkluzívne údaje, ktoré zahŕňajú rôzne situácie a vstupy. Na tento účel použi generátory údajov a kontrolu testovacích údajov, čím zabrániš opakovaniu.
Testovanie nesprávnych typov údajov
Významným úskalím pri unit testovaní je kontrola nesprávnych aspektov alebo vyhodnocovanie niečoho, čo nie je jednotkou. Vyhodnocovanie komponentov, ktoré nie sú jednotkami, ako sú databázové dotazy, volania webových služieb alebo používateľské rozhrania, vedie k nespoľahlivým a pomalým testom, ktoré sa spoliehajú na externé prvky.
Riešenie: Na efektívne unit testovanie použi mocking a stubs. Tieto techniky simulujú závislosti jednotky, čím vytvárajú kontrolované prostredie, ktoré izoluje jednotku od väčšieho systému.
Žiadne vzájomné hodnotenia
Vynechanie testovacích recenzií znamená, že sa vynechá zdieľanie osvedčených postupov a zvyšuje sa pravdepodobnosť výskytu testovacích chýb. Recenzie kódu zvyšujú kvalitu unit testov v tíme.
Riešenie: Pre optimálne výsledky vykonávaj priamo review kódu, či už osobne alebo prostredníctvom zdieľania obrazovky.
Vytváranie logických testov
Vkladanie logiky do unit testov komplikuje ich čitateľnosť a údržbu a zvyšuje riziko chýb. Ak tvoj unit test obsahuje logiku, naznačuje to, že možno nevytváraš správne unit testy.
Riešenie: V prípade, že sa v testovacích jednotkách nachádza nejaká chyba, je potrebné ju odstrániť: Obmedz počet tvrdení (assertions) v každom teste. Ak máš príliš veľa tvrdení, údržba sa stáva náročnou.
Testovanie externalít
Jedným zo spoľahlivých spôsobov, ako mať unit testy, ktorých spustenie trvá večnosť, je napísať unit testy, ktoré robia veci ako zápis súborov na disk alebo vyťahovanie informácií z databáz. Vyhni sa teda používaniu externých prvkov, pretože to spomalí test a aj preto, že keď to robíš, v skutočnosti nepíšeš unit testy. Unit testy sú cielené kontroly, ktoré izolujú kód a tvrdia, ako sa má správať. Kontroluj veci ako „ak metóde add(int, int) podám 2 a 2, vráti 4?“. To je rozsah unit testu.
Riešenie: Riešenie je jednoduché. Netestovať databázy, zápis na disk a podobne, testovať len malé časti kódu.
Záver
Ako vidíš, unit testovanie môže byť veľmi náročné. Testovanie komponentov je ale na určitej úrovni vždy potrebné. To je isté.
Ak sa chceš otestovať vo svojich znalostiach o unit testoch, alebo sa pripravuješ na pohovor, určite nepreskoč tento zoznam najčastejších otázok a odpovedí – unit test questions for interview.
Ak vieš po nemecky a hľadáš si prácu ako IT tester alebo automatizovaný tester, prezri si naše benefity pre zamestnancov a reaguj na najnovšie ponuky práce.