Nahoru
 

Automatické testování softwaru – neboli kód, co kontroluje kód

Při vývoji aplikace nemůžeme nikdy zaručit její stoprocentní stabilitu. Bohužel, programátoři při vývoji dělají spoustu chyb. Otestovat software musíme vždy před jeho vypuštěním a to několikrát, abychom si byli jistí, že jsme většinu errorů odhalili a napravili. U menších projektů není problém nechat kontrolu „bugů“ až nakonec, ale u těch větších se vyplatí testovat pravidelně a automaticky.

Pořádné a metodické testování má smysl zejména u velkých projektů vyvíjených velkým počtem programátorů. V prvé řadě je nutné si uvědomit, jak takový vývoj probíhá. Když máme jasně ustanovenou a zanalyzovanou funkčnost aplikace, započneme s jejím návrhem. Při návrhu rozdělíme program na jednotlivé oddělené části, jež spolu budou komunikovat. Dá se říci, že aplikaci „rozvrstvíme“. Kdybychom například vytvářeli složitý web, jasnými vrstvami by byly:

  • Prezentační vrstva (front-end)
  • Business logika (back-end)
  • Datová vrstva (databáze)

Pořád se ovšem jedná o velice hrubé rozčlenění a často jdeme hlouběji. V našem příkladu bychom např. dále rozdělili business logiku na jednotlivé balíčky tříd, kde lze samozřejmě každý balíček rozdělit i na samostatné třídy a ty zase mají své vlastní metody.

Rozdělení aplikace na její jednotlivé části
Obrázek č. 1: Příklad rozdělení aplikace na její jednotlivé části

Nyní zhruba víme, z čeho se skládá naše aplikace a můžeme se pustit do strategie testování. Možná si říkáte, proč v článku o testování softwaru mluvím o jeho návrhu. Je to z toho důvodu, že velké aplikace se nedají testovat pouze jako celek. Nestačí celou věc naprogramovat a potom zkoušet všechny možnosti.

Testování softwaru

Správné a důkladné testování softwaru znamená testovat samostatně jeho nejmenší oddělené části a následnou komunikaci mezi nimi. Poté pokračujeme tímto způsobem a jdeme o stupeň v hierarchii výš a výš, až nakonec budeme testovat komunikaci mezi jednotlivými vrstvami. V některých případech se dále testuje funkčnost na jiných zařízeních, či v různých prostředích.

Začneme s testováním nejmenších částí, což jsou ve většině případů metody tříd (případně funkce). Pokud máme například metodu pro výpočet daně za nějaké zboží, vymyslíme si celou řadu možných scénářů a ty vyzkoušíme. Neopomineme zkusit i extrémní případy nebo neplatné hodnoty.

Téměř vždy máme v projektu také třídy, jež spolu nějakým způsobem komunikují. Tato komunikace by měla být také otestována. Fungující metody z třídy A a z třídy B nezaručují, že třída C, která jen kombinuje metody třídy A a třídy B, funguje správně. Nadále pokračujeme v hierarchii výš až se dostaneme k samotným vrstvám. Pořád zde platí stejné pravidlo. V našem příkladu samostatně fungující prezentační vrstva a fungující business logika určitě nezaručuje fungující komunikaci mezi nimi.

Když předchozí postup stručně shrneme, dostáváme se k tomu, že testování softwaru jde ruku v ruce s jeho návrhem. Postupujeme vždy tak, že testujeme nejmenší možné oddělené části a poté postupně vyzkoušíme, jak spolu tyto části komunikují. Třídy umožňující komunikaci mezi jinými třídami je nutné otestovat s komunikací s dalšími třídami a podobně.

Při testování také nastává otázka, jak moc energie do něj věnovat, obzvláště do psaní automatického testování. Zde není snadné nalézt snadnou odpověď, avšak dle selského rozumu by určitě měly být otestovány ty části programu, které jsou nejvíce využívané, nebo na nich závisí velká část jiných struktur. Dalším rozhodujícím faktorem pro obsažnost testování je velikost samotné aplikace. Zatímco u malých programů není třeba testovat příliš do hloubky, u té velké rozhodně ano.

Automatické testování softwaru

Automatickým testováním softwaru rozumíme kód (tedy další software), který nějakým způsobem sám otestuje, zda naše aplikace funguje. Hlavním rozdílem mezi ručním testováním a automatickým testováním je nutnost samotné kontroly výsledku. U ručního testování se musíme sami ujistit, jestli je vše, tak jak má být. Automatický test to zjistí sám a nám poví pouze zda „prošel“, anebo „neprošel“.

Pro představu, jak může automatický test vypadat, uvádím následující příklad v jazyce C++. Mějme funkci, která ze dvou čísel vrátí to číslo, které má větší absolutní hodnotu. Pokud budou mít absolutní hodnotu stejnou, vrátíme první z nich. Kód by mohl vypadat nějak takto:

int greater_abs(int n1, int n2)
{
  int abs_n1 = n1;
  int abs_n2 = n2;

  if (abs_n1 < 0)
  {
    abs_n1 *= -1;
  }

  if (abs_n2 < 0)
  {
    abs_n2 *= -1; 
  }

  return abs_n1 >= abs_n2 ? n1 : n2;
}

Nyní chceme této funkci napsat automatický test. Děláme ho tak, že jednoduše funkci zavoláme s nějakými hodnotami a ozkoušíme, zda máme správný výsledek. Test by mohl vypadat takto:

void test_greater_abs()
{
  int res1 = greater_abs(10, 11);
  int res2 = greater_abs(10, 1);
  int res3 = greater_abs(0, 0);
  int res4 = greater_abs(1, -1);
  int res5 = greater_abs(1000000, -11);
  int res6 = greater_abs(-10, 1);

  // funkce assert zastaví program, pokud obdrží hodnotu „false“
  assert(res1 == 11); 
  assert(res2 == 10);
  assert(res3 == 0);
  assert(res4 == 1);
  assert(res5 == 1000000);
  assert(res6 == -10);

  printf("test: greater_abs() passed!\n");
}

Automatický test nyní můžeme spustit kdykoliv chceme a on za nás sám ozkouší, zda funkce funguje. Reálně samozřejmě neodpoví na otázku „Je tato funkce 100 % správně?“, ale ozkouší námi zadané hodnoty.

Nyní lze namítat, že takové testy můžeme přeci ozkoušet ručně vcelku jednoduše. To je sice pravda, jenže funkcí jako tahle, máme v našem obrovském projektu nesčetné množství. Navíc jsou provázány jedna na druhou atd. V případě, že někdy v budoucnu budeme kód upravovat, tyto testy nás pojistí v tom, že jsme nic nerozbili. Navíc nad nimi vůbec nemusíme přemýšlet, stačí je jen spustit a oni pracují sami.

Když už se rozhodneme testovat, tak je dobré testovat tzv. nemilosrdně. Naše testy mají za cíl nalézt, co nejvíce chyb a tím pádem zvýšit jeho kvalitu. Nalezená chyba vlastním testem znamená o mnoho problémů méně v budoucnu. Vždy se snažíme o to, aby v nejlepším případě zákazník žádnou chybu neobjevil.

Rozdělení testů

U rozsáhlých aplikací lze rozdělit testování do jednotlivých kategorií podle různých kritérií. Jedno takové rozdělení už jsme si zmínili výše, a to manuální a automatické testy. Jedná se konkrétně o kategorii způsobu vyhodnocování.

Dále lze testy řadit podle toho, zda známe zdrojový kód k danému programu či nikoliv. Pokud kód známe, provádíme tzv. white box testování. Pokud ho neznáme, označujeme testování jako black box.

Nejčastěji se testy rozdělují podle rozsahu. Nejmenším testům, které kontrolují správnost funkcí a metod tříd, se říká jednotkové testy. Testy, kontrolující komunikaci mezi třídami, se nazývají testy komponent. Ty jsou společně s jednotkovými testy hlavními adepty na automatizaci, protože testujeme pouze malou část aplikace. Posuneme-li se o úroveň výš, dostáváme integrační testy, které simulují reálné prostředí a testují spolupráci komponent naší aplikace. Téměř vždy probíhají ve standartním prostředí, například v kontejneru (pokud se chcete dozvědět více o kontejnerech, psali jsme o nich dříve článek "Kontejnery a virtualizace"). Do integračních testů patří mimo jiné například připojení aplikace k databázi. Největšími testy jsou systémové testy, které testují aplikaci jako celek. Typicky testujeme nějaký zadaný scénář (např. z use case diagramu) a kontrolujeme správnost výsledku manuálně.

Existují i spousty jiných rozdělení testů podle dalších kritérií. Pro začátek si však lze vystačit s těmito třemi. Pro shrnutí je zde seznam různých kategorií testů:

Podle způsobu vyhodnocování:
  • Manuální
  • Automatické
Znalosti vnitřní struktury:
  • White box
  • Black box
Rozsahu:
  • Jednotkové testy
  • Testy komponent
  • Integrační testy
  • Systémové testy

Testování aplikace je důležitou součástí každého většího vývoje a nemá se opomíjet. Část testování se také vyplatí zautomatizovat. Kvalita softwaru tím určitě vroste, a navíc získáme snadné otestování aplikace v budoucnu, když budeme do kódu zasahovat. Napsat pár testů ke svému vlastnímu kódu by měl zvládnout každý programátor.