Lekce 2 - Testování ve VB.NET - Úvod do unit testů a příprava projektu
V minulé lekci, Testování ve VB.NET - Úvod do testování, jsme si udělali poměrně solidní úvod do problematiky testování ve VB .NET. Také jsme si uvedli v-model, který znázorňuje vztah mezi jednotlivými výstupy fází návrhu a příslušnými testy.
V dnešním tutoriálu Testování ve VB .NET se budeme věnovat jednotkovým testům (unit testy). Unit testy testují detailní specifikaci aplikace, tedy její třídy.
Principy testování
Unit testy píšeme vždy na základě návrhu, nikoli implementace. Jinými slovy, děláme je na základě očekávané funkčnosti. Ta může být buď přímo od zákazníka (v případě akceptačních testů) nebo již od programátora (architekta). Ve funkčnosti je specifikováno, jak se má která metoda chovat.
Nikdy nepíšeme testy podle toho, jak je něco uvnitř naprogramované! Velmi jednoduše by to mohlo naše myšlení svést jen tím daným způsobem. Zapomněli bychom na to, že metodě mohou přijít třeba i jiné vstupy. A na tyto vstupy pak nebude připravená. Testování s implementací ve skutečnosti vůbec nesouvisí. Vždy testujeme, zda je splněno zadání.
Jaké třídy testujeme
Unit testy testují jednotlivé metody ve třídách. Nemá valný smysl testovat jednoúčelové metody např. v modelech, které např. pouze něco vybírají z databáze. Abychom byli konkrétnější, nemá smysl testovat metodu jako je tato:
Public Sub VlozPolozku(nazev As String, cena As Double) Using db As New DatabaseEntities() db.polozky.Add(New Polozka(nazev, cena)) db.SaveChanges() End Using End Sub
Metoda přidává položku do databáze. Typicky je použita jen v nějakém formuláři. Pokud by metoda nefungovala, zjistí to akceptační testy. Nová položka by se neobjevila v seznamu. Podobných metod je v aplikaci hodně. Zbytečně bychom ztráceli čas pokrýváním něčeho, co snadno pokryjeme v jiných testech.
Unit testy nalezneme nejčastěji u knihoven, které programátor používá na více místech, nebo dokonce ve více projektech. Když použijeme nějakou knihovnu (např. staženou z GitHubu), velmi pravděpodobně budou u ní také testy.
Pokud např. píšeme aplikaci, ve které často potřebujeme nějaké matematické výpočty (např. faktoriály a další pravděpodobnostní funkce), je samozřejmostí si vytvořit na tyto výpočty knihovnu. Je velmi dobrý nápad pokrýt takovou knihovnu testy.
Praktický příklad
V praktickém příkladu budeme tvořit třídu s metodami, které necháme otestovat. Abychom se nezdržovali, budeme tvořit pouze jednoduchou kalkulačku. Ta bude umět:
- sčítat,
- odčítat,
- násobit,
- dělit.
V praxi by ve třídě byly nějaké složitější výpočty, ale tím se zde nebudeme zabývat.
Testovaný projekt
Vytvořme si nový projekt typu konzolová
aplikace s názvem KalkulackaApp
. Do něj si přidáme
veřejnou (Public
) třídu Kalkulacka
s následující implementací:
''' <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 Function Secti(a As Double, b As Double) As Double Return a + b End Function ''' <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 Function Odecti(a As Double, b As Double) As Double Return a - b End Function ''' <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 Function Vynasob(a As Double, b As Double) As Double Return a * b End Function ''' <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 Function Vydel(a As Double, b As Double) As Double If b = 0 Then Throw New ArgumentException("Nulou nelze dělit!") End If Return a / b End Function End Class
Na kódu je zajímavá pouze metoda Vydel()
vyvolávající
výjimku v případě dělení nulou.
Testovací projekt
Do své aplikace budeme přidávat testy jako další projekt do solution. Testy tedy budou od projektu úplně oddělené, což je návrhově velká výhoda. Pouze nesmíme zapomenout propojit projekty příslušnými referencemi.
V okně Solution Explorer klikneme na Solution KalkulackaApp pravým tlačítkem a zvolíme Add -> New Project...:

V dalším kroku vybereme šablonu MSTest Test Project:

Posléze zvolíme název projektu s testy, který se
zpravidla sestavuje jako název projektu aplikace + slovo Tests
. V
našem případě tedy KalkulackaAppTests
:

A nakonec potvrdíme typ frameworku NET 6.0:

Do testovacího projektu nyní musíme přidat referenci na
projekt s aplikací, abychom mohli přistupovat k příslušným třídám. To
provedeme kliknutím pravým tlačítkem na projekt
KalkulackaAppTests
a volbou Add -> Project
Reference...:

V následujícím formuláři vybereme záložku Projects ->
Solution a zaškrtneme projekt KalkulackaApp
. Dialog
potvrdíme, čímž si zpřístupníme třídu Kalkulacka
:

V projektu KalkulackaAppTest
se nám vygeneroval nový soubor
UnitTest1.cs
s následujícím kódem:
Imports Microsoft.VisualStudio.TestTools.UnitTesting Namespace KalkulackaAppTests <TestClass> Public Class UnitTest1 <TestMethod> Sub TestSub() End Sub End Class End Namespace
Ve VB.NET se unit testy píší pomocí nástrojů z jmenného
prostoru Microsoft.VisualStudio.TestTools.UnitTesting
. Visual
Studio poskytuje plnou podporu těchto testů.
V objektovém VB.NET je test třídy (scénář) reprezentovaný také třídou a jednotlivé testy jsou reprezentovány metodami.
Atribut <TestClass>
zde označuje testovací
scénář. Atributem <TestMethod>
vidíme označené
metody, které reprezentují jednotlivé testy (budou
automaticky spouštěné Visual Studiem). Třídu UnitTest1
(i
její soubor) si přejmenujeme na KalkulackaTests
, jelikož bude
obsahovat testy pro třídu Kalkulacka
.
Pokrytí kódu testy
Do třídy KalkulackaTests
si nyní nejprve přidejme jmenný
prostor Imports KalkulackaApp
a pak Private
proměnnou
kalkulacka
. V metodě s anotací
<TestInitialize>
si do proměnné kalkulacka
vytvoříme novou kalkulačku pro každý test. Pokud by ji bylo ještě třeba
dále nastavovat (např. vytvořit další závislosti) byly by také v této
metodě. Metodu TestMethod1()
odstraníme.
Náš kód třídy KalkulackaTests
po změnách vypadá
následovně:
Imports Microsoft.VisualStudio.TestTools.UnitTesting Imports KalkulackaApp Namespace KalkulackaAppTests <TestClass> Public Class KalkulackaTests Private kalkulacka As Kalkulacka <TestInitialize> Public Sub Initialize() kalkulacka = New Kalkulacka() End Sub <TestCleanup> Public Sub Cleanup() End Sub End Class End Namespace
V kódu jsme použili atribut <TestInitialize>
a pro
názornost i atribut <TestCleanup>
. Těmito atributy
označujeme metody, které se zavolají před (resp. po každém
testu) v této třídě. To je pro nás velmi důležité. Chceme, aby
testy byly nezávislé. Obvykle tedy před každým testem
připravujeme znovu to samé prostředí, aby se testy vzájemně vůbec
neovlivňovaly.
Nyní máme vše připraveno k přidání samotných testů.
V příští lekci, Testování ve VB.NET - Dokončení unit testů a best practices, pokryjeme třídu unit testy, vysvětlíme si
metody na třídě Assert
a naučíme se testovat výjimky.
Zmíníme best practices.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.