IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

3. diel - Testovanie v C # .NET - Dokončenie unit testov a best practices

V minulej lekcii, Testovanie v C # .NET - Úvod do unit testov a príprava projektu , sme si pripravili jednoduchú triedu a vygenerovali testovacie projekt s potrebnou referenciou. Dnes pokryjeme testy našu jednoduchú triedu, uvedieme si dostupné asserční metódy a unit testy v C# .NET dovŕšite prehľadom best practices.

Jednotlivé metódy budú vždy označené atribútom [TestMethod] a budú testovať jednu konkrétnu metódu z triedy Kalkulacka, typicky pre niekoľko rôznych vstupov. Ak vás napadá prečo metódy označujeme atribúty, umožňuje nám to vytvoriť si aj pomocné metódy, ktoré môžeme v danom teste využívať a ktoré nebudú považované za testy. Visual Studio nám totiž testy (metódy s anotáciou [TestMethod]) automaticky spustí a vypíše ich výsledky.

Pridajme nasledujúcich 5 metód do triedy KalkulackaTests:

[TestMethod]
public void Secti()
{
    Assert.AreEqual(2, kalkulacka.Secti(1, 1));
    Assert.AreEqual(1.42, kalkulacka.Secti(3.14, -1.72), 0.001);
    Assert.AreEqual(2.0 / 3, kalkulacka.Secti(1.0 / 3, 1.0 / 3), 0.001);
}

[TestMethod]
public void Odecti()
{
    Assert.AreEqual(0, kalkulacka.Odecti(1, 1));
    Assert.AreEqual(4.86, kalkulacka.Odecti(3.14, -1.72), 0.001);
    Assert.AreEqual(2.0 / 3, kalkulacka.Odecti(1.0 / 3, -1.0 / 3), 0.001);
}

[TestMethod]
public void Vynasob()
{
    Assert.AreEqual(2, kalkulacka.Vynasob(1, 2));
    Assert.AreEqual(-5.4008, kalkulacka.Vynasob(3.14, -1.72), 0.001);
    Assert.AreEqual(0.111, kalkulacka.Vynasob(1.0 / 3, 1.0 / 3), 0.001);
}

[TestMethod]
public void Vydel()
{
    Assert.AreEqual(2, kalkulacka.Vydel(4, 2));
    Assert.AreEqual(-1.826, kalkulacka.Vydel(3.14, -1.72), 0.001);
    Assert.AreEqual(1, kalkulacka.Vydel(1.0 / 3, 1.0 / 3));
}

[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void VydelVyjimka()
{
    kalkulacka.Vydel(2, 0);
}

K porovnávanie výstupu metódy s očakávanou hodnotou používame statickej metódy na triede Assert. Najčastejšie asi použijete metódu AreEqual(), ktorá prijíma ako prvý parameter očakávanú hodnotu a ako druhý parameter hodnotu aktuálny. Toto poradie je dobré dodržiavať, inak budete mať hodnoty vo výsledkoch testov opačne. Ako asi viete, desatinné čísla sú v pamäti počítača reprezentovaná binárne (ako inak :) ) A to spôsobí určitú stratu ich presnosti a tiež určité ťažkosti pri ich porovnávaní. Preto musíme v tomto prípade zadať aj tretí parameter a to je delta, teda kladná tolerancia, o koľko sa môže očakávaná a aktuálna hodnota líšiť, aby test stále prešiel.

Všimnite si, že skúšame rôzne vstupy. Sčítanie netestujeme len ako 1 + 1 = 2, ale skúsime celočíselné, desatinné aj negatívne vstupy, oddelene, a overíme výsledky. V niektorých prípadoch by nás mohla zaujímať tiež maximálna hodnota dátových typov a podobne.

Posledný test overuje, či metóda Vydel() naozaj vyvolá výnimku pri nulovom deliteľmi. Ako vidíte, nemusíme sa zaťažovať s try-catch bloky, stačí nad metódu pridať atribút [ExpectedException] a uviesť tu typ výnimky, ktorá sa očakáva. Ak výnimka nenastane, test zlyhá. Pre testovanie viac prípadov vyvolanie výnimky môžeme použiť ďalšie Assert metódy, viď nižšie.

Dostupné Assert metódy

Bolo by vhodné spomenúť, že sa porovnáva s ohľadom na dátové typy, teda 10L (long) je iná hodnota ako 10 (int). Okrem metódy AreEqual() môžeme použiť ešte mnoho ďalších, určite sa snažte použiť tú najviac vyhovujúce metódu, sprehľadňuje to hlášky pri zlyhaní testov a samozrejme aj následnú opravu.

  • AreNotEqual() - Používame ak chceme overiť, že sa 2 objekty nezhodujú. Ďalšie metódy s Not tu už nebudeme zbytočne zmieňovať.
  • AreSame() - Skontroluje, či 2 referencie ukazujú na rovnaký objekt (porovnáva pomocou ==).
  • Equals() - Používame v provy, keď chceme overiť 2 objekty pomocou metódy Equals() a zistiť, či sú rovnaké. Nepoužívame pre overenie hodnoty miesto AreEqual().
  • Fail() - Spôsobí zlyhanie testov, zvyčajne vkladáme za nejakú podmienku a dopĺňame o voliteľné parametre, ktorými sú chybová hláška a parametre.
  • Inconclusive() - Funguje podobne ako Fail(), vyvolá výnimku signalizujúci nepreukázateľnosť testu.
  • IsFalse() - Overuje, či je daný výraz nepravdivá.
  • IsInstanceOfType() - Overuje, či je objekt inštanciou daného typu.
  • IsNull() - Overuje, či je hodnota null.
  • IsTrue() - Overuje, či je daný výraz pravdivý.
  • ReplaceNullChars() - Nahradí nullové znaky ( "\ 0") za "\\ 0", využijeme najmä u diagnostických výpisov reťazcov s týmito znakmi.
  • ThrowsException() - Spustí odovzdaný delegát a overí, že vyvoláva výnimku odovzdanú ako generický argument. Metóda má tiež asynchrónne verziu ThrowsExceptionAsync().

Nenechajte sa zmiasť metódou ReferenceEquals(), ktorá nie je súčasťou testov, ale je štandardne na všetkých triedach.

Spustenie testov

Testy spustíme z menu Test -> Run -> All Tests:

Spustenie unit testov vo Visual Studio - Testovanie v C # .NET

Uvidíme výsledky, ktoré vyzerajú nejako takto:

Výsledky unit testov vo Visual Studio - Testovanie v C # .NET

Skúsme si teraz urobiť v kalkulačke chybu, napr. Zakomentujte vyvolávanie výnimky pri delení nulou a vráťme vždy hodnotu 1:

public double Vydel(double a, double b)
{
    // if (b == 0)
    //  throw new ArgumentException("Nulou nelze dělit!");
    return 1;
}

A spustite znovu naše testy:

Chyba v C# .NET unit testoch - Testovanie v C # .NET

Vidíme, že chyba je zachytená a sme na ňu upozornení. Neprešiel ako test delenia, tak test vyvolanie výnimky. Môžeme kód vrátiť späť do pôvodného stavu.

Best practices

Už v minulej lekcii sme načali best practices. Keďže to je k unit testom v C# .NET všetko, poďme si na záver vymenovat akých častých chýb sa vyvarovať, aby sme dosiahli kvalitného výsledku.

  • Testujeme špecifikáciu, nie kód. Testy nikdy nepíšeme podľa kódu nejaké metódy, ale zamýšľame sa nad tým, k čomu metóda reálne slúži a čo všetko ju môže prísť ako vstup.
  • Testujeme všeobecné knižnice, nie konkrétnej logiku aplikácie. Ak je logika dôležitá a všeobecná, mala by byť vyčlenená do samostatnej knižnice a tá by mala byť potom testovaná.
  • Každý test by mal byť úplne nezávislý od ostatných testoch. Scenár by mal prebehnúť aj keď metódy ľubovoľne proházíme a žiadna metóda by po sebe nemala zanechávať nejaké zmeny (v súboroch, v databáze a podobne), ktoré by ovplyvnili ďalšie metódy. Na dosiahnutie tohto správania často pripravujeme prostredie pre jednotlivé metódy v inicializačnú metóde a prípadne po nich ešte prevedieme upratovanie v metóde upratovacie. To isté platí aj pre celé testy.
  • Každý test by mal dopadnúť vždy rovnako, bez ohľadu na to, kedy ho spustíme. Pozor na testovanie generátorov náhodných výstupov a na prácu s dátumom a časom.
  • Nevykonávajte duplicitné porovnanie, ak nejaký vstup už overuje iný test, nerobte toto overenie znovu.
  • Každý scenár testuje len jednu jednotku (triedu). Váš softvér by mal byť navrhnutý tak, aby bol rozdelený na menšie triedy, ktoré majú minimálne závislosti na ostatných a preto sa dajú jednoducho a nezávisle testovať (vzory high cohesion a low coupling).
  • Ak testy vyžadujú externé služby, mali by sme ich tzv. Mocková. Tým vytvárame "falošné" služby s rovnakým rozhraním, ktoré zvyčajne len podstrkuje testovacie dáta. Využitím skutočných služieb by sme porušili nezávislosť testov, pretože by sa navzájom začali ovplyvňovať. Menej elegantné riešenie je vždy na začiatku nastaviť a na konci vrátiť stav služieb.
  • Ako platí aj všade inde, vyhnite sa zavádzajúcim názvom testov (ako vypocet(), vyjimka() a podobne). Programátori často pomenúvajú testy aj väčším počtom slov, aby sa spoznalo čo robia. Bežne by sme to pri metódach nemali robiť, pretože každá metóda robí len jednu činnosť, ale u testov dáva niekedy zmysel pomenovať metódy napr. Aj takto Obskurní KvadratickaRovnice_ZaporneKoeficienty_Vyjimka(), pretože test často testuje viac vstupov. Ideálne by mal názov testu obsahovať názov metódy, ktorú testuje. V pomenovávanie testov by ste mali byť konzistentné. Nebojte sa ani komentárov.
  • Testy by mali prebehnúť rýchlo, pretože v praxi zvyčajne testujeme všetky časti aplikácie rôznymi typmi testov a všetky časy sa dokážu sčíta do nepríjemnej pauzy.

Vaša prvá unit testy nemusí byť perfektné, stačí krátko otestovať to najdôležitejšie. Uvidíte, že vám časom začnú zlyhávať a odhaľovať chyby v implementácii. Čím je aplikácia väčšia, tým o väčšie pokrytie testy (test code coverage) by sme sa mali snažiť. V budúcej lekcii, Úvod do akceptačných testov a tvorba GUI pre kalkulačku v C # , sa pozrieme na testy akceptačnej.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 146x (1.89 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
Testovanie v C # .NET - Úvod do unit testov a príprava projektu
Všetky články v sekcii
Testovanie v C # .NET
Preskočiť článok
(neodporúčame)
Úvod do akceptačných testov a tvorba GUI pre kalkulačku v C #
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
2 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity