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ódyEquals()
a zistiť, či sú rovnaké. Nepoužívame pre overenie hodnoty miestoAreEqual()
.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 akoFail()
, 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 hodnotanull
.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 verziuThrowsExceptionAsync()
.
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:
Uvidíme výsledky, ktoré vyzerajú nejako takto:
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:
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#