2. diel - Testovanie v C # .NET - Úvod do unit testov a príprava projektu
V minulej lekcii, Úvod do testovania softvéru v C # .NET , sme si urobili pomerne solídny úvod do problematiky. Tiež sme si uviedli v-model, ktorý znázorňuje vzťah medzi jednotlivými výstupmi fázou návrhu a príslušnými testami.
Testy teda píšeme vždy na základe návrhu, nie implementácie. Inými slovami, robíme je na základe očakávanej funkčnosti. Tá môže byť buď priamo od zákazníka (a to v prípade akceptačných testov) alebo už od programátora (architekta), kde špecifikuje ako sa má ktorá metóda správať. Dnes sa budeme venovať práve týmto testom, ktorým hovoríme jednotkové (unit testy) a ktoré testujú detailnú špecifikáciu aplikácie, teda jej triedy.
Pamätajte, že nikdy nepíšeme testy podľa toho, ako je niečo vnútri naprogramované! Veľmi jednoducho by to mohlo naše myslenie zviesť len tým daným spôsobom a zabudli by sme na to, že metóde môžu prísť napríklad aj iné vstupy, na ktoré nie je vôbec pripravená. Testovanie s implementáciou v skutočnosti vôbec nesúvisí, vždy testujeme či je splnené zadanie.
Aké triedy testujeme
Unit testy testujú jednotlivé metódy v triedach. Pre istotu zopakujem, že nemá veľký zmysel testovať jednoúčelové metódy napr. V modeloch, ktoré napr. Len niečo vyberajú z databázy. Aby sme boli konkrétnejší, nemá zmysel testovať metódu ako je táto:
public void VlozPolozku(string nazev, double cena) { using (var db = new DatabaseEntities()) { db.polozky.Add(new Polozka(nazev, cena)); db.SaveChanges(); } }
Metóda pridáva položku do databázy. Typicky je použitá len v nejakom formulári a ak by nefungovala, zistí to akceptačné testy, pretože by sa nová položka neobjavila v zozname. Podobných metód je v aplikácii veľa a zbytočne by sme strácali čas pokrývaním niečoho, čo ľahko pokryjeme v iných testoch.
Unit testy nájdeme najčastejšie u knižníc, teda nástrojov, ktoré programátor používa na viacerých miestach alebo dokonca vo viacerých projektoch a mali by byť 100% funkčný. Možno si spomeniete, kedy ste použili nejakú knižnicu, stiahnutú napr. Z GitHub. Veľmi pravdepodobne u nej boli tiež testy. Ak napr. Píšeme aplikáciu, v ktorej často potrebujeme nejaké matematické výpočty, napr. Faktoriál a ďalšie pravdepodobnostné funkcie, je samozrejmosťou vytvoriť si na tieto výpočty knižnicu a je veľmi dobrý nápad pokryť takú knižnicu testy.
Príklad
Ako asi tušíte, my si podobnú triedu vytvoríme a skúsime si ju otestovať. Aby sme sa nezdržovali, vytvorme si iba jednoduchú kalkulačku, ktorá bude vedieť:
- sčítať
- odčítať
- násobiť
- deliť
Vytvorenie projektu
V praxi by v triede boli nejaké zložitejšie výpočty, ale tým sa tu
zaoberať nebudeme. Vytvorte si nový projekt, konzolovú aplikáciu, s názvom
KalkulackaApp
. Do neho si pridajte verejnú
(public
) triedu Kalkulacka
a nasledujúce
implementácií:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace KalkulackaApp { /// <summary> /// Reprezentuje jednoduchou kalkulačku /// </summary> public class Kalkulacka { /// <summary> /// Sečte 2 čísla /// </summary> /// <param name="a">První číslo</param> /// <param name="b">Druhé číslo</param> /// <returns>Součet 2 čísel</returns> public double Secti(double a, double b) { return a + b; } /// <summary> /// Odečte 2 čísla /// </summary> /// <param name="a">První číslo</param> /// <param name="b">Druhé číslo</param> /// <returns>Rozdíl 2 čísel</returns> public double Odecti(double a, double b) { return a - b; } /// <summary> /// Vynásobí 2 čísla /// </summary> /// <param name="a">První číslo</param> /// <param name="b">Druhé číslo</param> /// <returns>Součin 2 čísel</returns> public double Vynasob(double a, double b) { return a * b; } /// <summary> /// Vydělí 2 čísla /// </summary> /// <param name="a">První číslo</param> /// <param name="b">Druhé číslo</param> /// <returns>Podíl 2 čísel</returns> public double Vydel(double a, double b) { if (b == 0) throw new ArgumentException("Nulou nelze dělit!"); return a / b; } } }
Na kódu je zaujímavá iba metóda Vydel()
, ktorá vyvolá
výnimku v prípade, že delíme nulou. Predvolené správanie C# .NET pre
desatinné čísla je vrátenie hodnoty "Infinity", čo nie je vždy to, čo
používateľ aplikácie očakáva.
UnitTesting
VC # .NET sa unit testy píšu pomocou nástrojov v mennom priestore
Microsoft.VisualStudio.TestTools.UnitTesting
. Visual Studio
poskytuje plnú podporu týchto testov a k svojej aplikácii je pridáme ako
ďalší projekt do solution. Testy teda budú od projektu úplne oddelené, čo
je návrhové veľká výhoda, iba nesmieme zabudnú projekty prepojiť
príslušnými referenciami.
V Solution Exploreri klikneme na solution "KalkulackaApp" pravým tlačidlom a zvolíme Add -> New Project ....
Názov projektu s testami sa spravidla zostavuje ako názov projektu aplikácie + slovo "Tests", v našom prípade teda "KalkulackaAppTests":
Do testovacieho projektu teraz musíme pridať referenciu na projekt s
aplikáciou, aby sme mohli pristupovať k príslušným triedam. To vykonáme
kliknutím pravým tlačidlom na projekt KalkulackaAppTests
a
zvolením Add -> Referencie ...
V nasledujúcom formulári vyberieme záložku Projects -> Solution a
zaškrtneme projekt KalkulackaApp. Dialóg potvrdíme a tým si sprístupníme
triedu Kalkulacka
.
V projekte KalkulackaAppTest sa nám vygeneroval nový súbor UnitTest1 s nasledujúcim kódom:
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace KalkulackaAppTests { [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { } } }
Asi vás v objektovom C# neprekvapí, že je test triedy (scenár)
reprezentovaný tiež triedou a jednotlivé testy metódami S atribúty (hranatých
zátvorkách nad metódami a triedami) sme sa už v seriáli stretli a vieme,
že slúži k dospecifikování nejakých informácií. [TestClass]
tu označuje testovacie scenár, pomocou [TestMethod]
sú
označené metódy, ktoré reprezentujú jednotlivé testy (budú automaticky
spúšťané Visual Studiom). Triedu (i jej súbor) si premenujte na
KalkulackaTests
, pretože bude obsahovať testy pre triedu
Kalkulacka
.
Pokrytie triedy testy
V unit testoch môžeme použiť ešte niekoľko atribútov. My teraz
využijeme [TestInitialize]
a pre názornosť i
[TestCleanup]
, čím môžeme označiť metódy, ktoré sa
zavolajú pred, resp. po každom teste v tejto triede. To je
pre nás veľmi dôležité, pretože podľa best practices chceme, aby boli
testy nezávislé. Obvykle teda pred každým testom
pripravujeme znovu to isté prostredie, aby sa vzájomne vôbec neovplyvňovali.
O dobrých praktikách sa zmienime detailnejšie neskôr. Do triedy si pridajme
field (atribút triedy v názvosloví OOP) kalkulacka
a v metóde s
anotáciou [TestInitialize]
v ňom vždy vytvorme čerstvo novú
kalkulačku pre každý test. Pokiaľ by ju bolo ešte potrebné ďalej
nastavovať alebo bolo treba vytvoriť ďalšie závislosti, boli by tiež v
tejto metóde. Metódu TestMethod1()
odstránime. Pre kalkulačku
pridáme using KalkulackaApp;
:
using System; using KalkulackaApp; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace KalkulackaAppTests { [TestClass] public class KalkulackaTests { private Kalkulacka kalkulacka; [TestInitialize] public void Initialize() { kalkulacka = new Kalkulacka(); // Vytvoří novou kalkulačku před každým testem } [TestCleanup] public void Cleanup() { } }
Máme všetko pripravené na pridávanie samotných testov, čo urobíme v budúcej lekcii, Testovanie v C # .NET - Dokončenie unit testov a best practices .