Lekce 3 - Testování ve VB.NET - Dokončení unit testů a best practices
V minulé lekci, Testování ve VB.NET - Úvod do unit testů a příprava projektu, jsme si připravili jednoduchou třídu a vygenerovali testovací projekt s potřebnou referencí.
V dnešním tutoriálu Testování ve VB .NET pokryjeme testy naši jednoduchou třídu. Uvedeme si dostupné asserční metody a unit testy ve VB .NET dovršíme přehledem best practices.
Jednotlivé metody budeme vždy označovat atributem
<TestMethod>
. Zajistíme tím, že se bude testovat jedna
konkrétní metoda z třídy Kalkulacka
. Typicky pro několik
různých vstupů.
Metody označujeme atributy. Umožňuje nám
to vytvořit si i pomocné metody, které můžeme v daném
testu využívat. Nebudou pokládány za testy. Visual Studio nám totiž testy
(metody s anotací <TestMethod>
) automaticky spustí a
vypíše jejich výsledky.
Metody třídy
KalkulackaTests
Do našeho projektu (do třídy KalkulackaTests
) přidáme pod
metodu Cleanup()
pět následujících metod:
<TestMethod> Public Sub Secti() Assert.AreEqual(2, CInt(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) End Sub <TestMethod> Public Sub Odecti() Assert.AreEqual(0, CInt(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) End Sub <TestMethod> Public Sub Vynasob() Assert.AreEqual(2, CInt(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) End Sub <TestMethod> Public Sub Vydel() Assert.AreEqual(2, CInt(kalkulacka.Vydel(4, 2))) Assert.AreEqual(-1.826, kalkulacka.Vydel(3.14, -1.72), 0.001) Assert.AreEqual(1, CInt(kalkulacka.Vydel(1.0 / 3, 1.0 / 3))) End Sub <TestMethod> <ExpectedException(GetType(ArgumentException))> Public Sub VydelVyjimka() kalkulacka.Vydel(2, 0) End Sub
K porovnávání výstupu metody s očekávanou hodnotou používáme
statické metody na třídě Assert
. Nejčastěji
používáme metodu AreEqual()
přijímající v prvním parametru
očekávanou hodnotu a v druhém parametru hodnotu
aktuální.
Pořadí parametrů je dobré dodržovat, jinak budeme mít hodnoty ve výsledcích testů opačně.
Desetinná čísla jsou v paměti počítače reprezentována binárně (jak jinak ). To způsobí určitou ztrátu jejich přesnosti, a také určité obtíže při jejich porovnávání. Proto musíme v tomto případě zadat i třetí parametr. Tímto parametrem je delta, tedy kladná tolerance. O kolik se může očekávaná a aktuální hodnota lišit, aby test stále prošel.
Zkoušíme různé vstupy. Sčítání netestujeme jen jako
1 + 1 = 2
. Zkoušíme celočíselné,
desetinné i negativní vstupy. Vše
odděleně s ověřením výsledků. V některých případech by nás mohla
zajímat také maximální hodnota datových typů a podobně.
Poslední test ověřuje, zda metoda Vydel()
opravdu vyvolá
výjimku při nulovém děliteli. Nemusíme se zatěžovat s
try-catch
bloky. Stačí nad metodu přidat atribut
<ExpectedException>
. Uvést zde typ
výjimky, která se očekává. Pokud výjimka nenastane, test selže.
Pro testování více případů vyvolání výjimky můžeme použít další
assert metody, viz níže.
Dostupné Assert
metody
Bylo by vhodné zmínit, že se porovnává s ohledem na datové typy. Tedy
10L
(long
) je jiná hodnota, než 10
(int
). Kromě metody AreEqual()
můžeme použít
ještě mnoho dalších. Snažíme se použít tu nejvíce vyhovující metodu.
To totiž zpřehledňuje hlášky při selhání testů a
samozřejmě i následnou opravu.
Uveďme si některé dostupné Assert
metody:
AreNotEqual()
- Používáme pokud chceme ověřit, že se 2 objekty NEshodují. Další metody sNot
zde již nebudeme zbytečně zmiňovat.AreSame()
- Zkontroluje zda 2 reference ukazují na stejný objekt (porovnává pomocí operátoru==
).Equals()
- Používáme v případě, pokud chceme ověřit 2 objekty pomocí metodyEquals()
a zjistit, jestli jsou stejné. Nepoužíváme pro ověření hodnoty místoAreEqual()
.Fail()
- Způsobí selhání testů. Obvykle ji vkládáme za nějakou podmínku. Doplňujeme o volitelné parametry (chybová hláška a parametry).Inconclusive()
- Funguje podobně jakoFail()
. Vyvolá výjimku signalizující neprůkaznost testu.IsFalse()
- Ověří, zda je daný výraz NEpravdivý.IsInstanceOfType()
- Ověří, zda je objekt instancí daného typu.IsNull()
- Ověří, zda je hodnotanull
.IsTrue()
- Ověří, zda je daný výraz pravdivý.ReplaceNullChars()
- Nahradí nulové znaky\0
za\\0
. Využijeme zejména u diagnostických výpisů řetězců s těmito znaky.ThrowsException()
- Spustí předaný delegát a ověří, že vyvolává výjimku předanou jako generický argument. Metoda má také asynchronní verziThrowsExceptionAsync()
.
Nenechme se zmást metodou ReferenceEquals()
. Není
součástí testů, ale je standardně na všech třídách.
Spuštění testů
Testy spustíme z menu Test -> Run All Tests:
Uvidíme výsledky, které vypadají takto:
Zkusme si nyní udělat v kalkulačce chybu. Např. zakomentujme
vyvolávání výjimky při dělení nulou a vraťme vždy hodnotu
1
:
Public Function Vydel(a As Double, b As Double) As Double 'If b = 0 Then 'Throw New ArgumentException("Nulou nelze dělit!") 'End If Return 1 End Function
A spusťme znovu naše testy:
Vidíme, že chyba je zachycena. Jsme na ni upozorněni. Neprošel jak test dělení, tak test vyvolání výjimky. Můžeme kód vrátit zpět do původního stavu.
Best practices
Best practices jsme již nakousli. Toto je k unit testům ve VB .NET vše. Pojďme si na závěr vyjmenovat jakých častých chyb se vyvarovat, abychom dosáhli kvalitního výsledku.
Best practices probíráme detailněji v kurzu Best practices pro návrh softwaru.
Specifikace testů
Testy nikdy nepíšeme podle kódu nějaké metody. Zamýšlíme se nad tím, k čemu metoda reálně slouží. Také nad tím, co vše ji může přijít jako vstup.
Testování obecných knihoven
Netestujeme konkrétní logiku aplikace. Pokud je logika důležitá a obecná, měla by být vyčleněná do samostatné knihovny. Ta by měla být poté testována.
Nezávislost testů
Každý test by měl být úplně nezávislý na ostatních testech. Scénář by měl proběhnout i když metody libovolně proházíme. Žádná metoda by po sobě neměla zanechávat nějaké změny (v souborech, v databázi a podobně), které by ovlivnily další metody. K dosažení tohoto chování často připravujeme prostředí pro jednotlivé metody v inicializační metodě. Případně po nich ještě provedeme úklid v metodě úklidové. To samé platí i pro celé testy.
Stabilita testů
Každý test by měl dopadnout vždy stejně. Bez ohledu na to, kdy jej spustíme. Pozor na testování generátorů náhodných výstupů a na práci s datem a časem.
Duplicitní porovnání
Pokud nějaký vstup již ověřuje jiný test, neprovádějme toto ověření znovu.
Jednotkové testy a návrh software
Náš software by měl být navržený tak, aby byl rozdělený na menší třídy. Ty mají minimální závislosti na ostatních. Proto se dají jednoduše a nezávisle testovat (vzory high cohesion a low coupling).
Externí služby
Měli bychom je tzv. mockovat. Tím vytváříme "falešné" služby se stejným rozhraním, které obvykle jen podstrkují testovací data. Využitím skutečných služeb bychom porušili nezávislost testů, jelikož by se navzájem začaly ovlivňovat. Méně elegantní řešení je, vždy na začátku nastavit a na konci vrátit stav služeb.
Názvy testů
Vyhněme se zavádějícím názvům testů (jako
vypocet()
, vyjimka()
a podobně). Programátoři
často pojmenovávají testy i větším počtem slov, aby se poznalo, co
dělají. Běžně bychom to u metod neměli dělat, jelikož každá metoda
dělá jen jednu činnost. Někdy u testů dává smysl pojmenovat metody např.
i takto obskurně KvadratickaRovnice_ZaporneKoeficienty_Vyjimka()
.
Test často testuje více vstupů. Ideálně by měl název testu
obsahovat název metody, kterou testuje. V pojmenovávání testů
bychom měli být konzistentní. Nebojme se ani
komentářů.
Rychlost testů
V praxi obvykle testujeme všechny části aplikace různými typy testů a všechny časy se dokáží nasčítat do nepříjemné pauzy.
Naše první unit testy nemusí být perfektní. Stačí krátce otestovat to nejdůležitější. Časem se začnou odhalovat chyby v implementaci. Čím je aplikace větší, tím o větší pokrytí testy (test code coverage) bychom se měli snažit.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 3x (2.17 MB)
Aplikace je včetně zdrojových kódů v jazyce VB.NET