IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

2. diel - Testovanie v C# .NET - Úvod do unit testov

V minulej lekcii, Testovanie v C# .NET - Úvod do testovania , sme si urobili pomerne solídny úvod do problematiky testovania v C# .NET. Tiež sme si uviedli v-model, ktorý znázorňuje vzťah medzi jednotlivými výstupmi fáz návrhu a príslušnými testami.

V dnešnom tutoriále Testovania v C# .NET sa budeme venovať jednotkovým testom (unit testy), ktoré testujú detailnú špecifikáciu aplikácie, teda jej triedy.

Unit testy teda píšeme vždy na základe návrhu, nie implementácie. Inými slovami, robíme ich 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). Vo funkčnosti je špecifikované, ako sa má ktorá metóda správať.

Pamätajme si, že nikdy nepíšeme testy podľa toho, ako je niečo vo 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. Nemá veľký zmysel testovať jednoúčelové metódy napr. v modeloch, ktoré napr. iba niečo vyberajú z databázy. Aby sme boli konkrétnejší, nemá zmysel testovať metódu ako je táto:

public void InsertItem(string name, double price)
{
    using (var db = new DatabaseEntities())
    {
        db.items.Add(new Item(name, price));
        db.SaveChanges();
    }
}

Metóda pridáva položku do databázy. Typicky je použitá len v nejakom formulári. Ak by metóda 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 pri knižniciach, teda nástrojoch, ktoré programátor používa na viacerých miestach alebo dokonca vo viacerých projektoch a mali by byť 100% funkčné. Keď použijeme nejakú knižnicu, napr. stiahnutú z GitHubu, veľmi pravdepodobne budú u nej aj testy.

Pokiaľ napr. píšeme aplikáciu, v ktorej často potrebujeme nejaké matematické výpočty (napr. faktoriály 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úto knižnicu testami.

My si podobnú triedu vytvoríme a skúsime si ju otestovať. Aby sme sa nezdržiavali, budeme tvoriť 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. Vytvorme si nový projekt, konzolovú aplikáciu, s názvom CalculatorApp. Do neho si pridáme verejnú (public) triedu Calculator s nasledujúcou implementáciou:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CalculatorApp
{
    /// <summary>
    /// Represents a simple calculator
    /// </summary>
    public class Calculator
    {
        /// <summary>
        /// Adds 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Sum of 2 numbers</returns>
        public double Add(double a, double b)
        {
            return a + b;
        }

        /// <summary>
        /// Subtracts 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Difference of 2 numbers</returns>
        public double Subtract(double a, double b)
        {
            return a - b;
        }

        /// <summary>
        /// Multiplies 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Product of 2 numbers</returns>
        public double Multiply(double a, double b)
        {
            return a * b;
        }

        /// <summary>
        /// Divides 2 numbers
        /// </summary>
        /// <param name="a">First number</param>
        /// <param name="b">Second number</param>
        /// <returns>Quotient of 2 numbers</returns>
        public double Divide(double a, double b)
        {
            if (b == 0)
                throw new ArgumentException("Cannot divide by zero!");
            return a / b;
        }

    }
}

Na kóde je zaujímavá iba metóda Divide(), 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

V C# .NET sa unit testy píšu pomocou nástrojov v mennom priestore Microsoft.VisualStudio.TestTools.UnitTesting. Visual Studio poskytuje plnú podporu týchto testov ak svojej aplikácii ich pridáme ako ďalší projekt do solution. Testy teda budú od projektu úplne oddelené, čo je návrhovo veľká výhoda. Iba nesmieme zabudnúť projekty prepojiť príslušnými referenciami.

V Solution Explorer klikneme na Solution CalculatorApp pravým tlačidlom a zvolíme Add -> New Project...

Přidání projektu do řešení - Testovanie v C# .NET

Vyberieme šablónu MSTest Test Project:

Výběr šablony projektu - Testovanie v C# .NET

Názov projektu s testami sa spravidla zostavuje ako názov projektu aplikácie + slovo "Tests", v našom prípade teda CalculatorAppTests:

Název projektu - Testovanie v C# .NET

V ďalšom okne potvrdíme typ frameworku NET 6.0:

testkalk_potvr_nazev_typ - Testovanie v C# .NET

Do testovacieho projektu teraz musíme pridať referenciu na projekt s aplikáciou, aby sme mohli pristupovať k príslušným triedam. To urobíme kliknutím pravým tlačidlom na projekt CalculatorAppTes­ts a zvolením Add - > Project Reference...

testkalk_ref_pridani - Testovanie v C# .NET

V nasledujúcom formulári vyberieme záložku Projects -> Solution a zaškrtneme projekt CalculatorApp. Dialóg potvrdíme a tým si sprístupníme triedu Calculator:

testkalk_ref_potvrzeni - Testovanie v C# .NET

V projekte CalculatorAppTests sa nám vygeneroval nový súbor UnitTest1.cs s nasledujúcim kódom:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CalculatorAppTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

Asi nás v objektovom C# neprekvapí, že je test triedy (scenár) reprezentovaný aj triedou a jednotlivé testy metódami :) S atribútmi (hranatými zátvorkami nad metódami a triedami) sme sa už v seriáli stretli a vieme, že slúžia na špecifikovanie nejakých informácií.

Atribút [TestClass] tu označuje testovací scenár. Pomocou atribútu [TestMethod] označujeme metódy, ktoré reprezentujú jednotlivé testy (budú automaticky spúšťané Visual Studiom). Triedu UnitTest1 (aj jej súbor) si premenujeme na CalculatorTests, pretože bude obsahovať testy pre triedu Calculator.

Pokrytie triedy testy

V unit testoch môžeme použiť ešte niekoľko ďalších atribútov. My teraz využijeme atribút [TestInitialize] a pre názornosť aj atribút [TestCleanup]. Týmito atribútmi označujeme 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 znova 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 najskôr pridajme menný priestor using CalculatorApp. Teraz si už môžeme, v triede CalculatorTests, vytvoriť private premennú calculator.

V metóde s anotáciou [TestInitialize] si do premennej calculator vytvoríme čerstvo novú kalkulačku pre každý test. Ak by ju bolo ešte potrebné ďalej nastavovať, alebo bolo treba vytvoriť ďalšie závislosti, boli by aj v tejto metóde. Metódu TestMethod1() odstránime.

Kód triedy CalculatorTests je nasledovný:

using System;
using CalculatorApp;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CalculatorAppTests
{
    [TestClass]
    public class CalculatorTests
    {
        private Calculator calculator;

        [TestInitialize]
        public void Initialize()
        {
            calculator = new Calculator(); // Creates a new calculator before each test
        }

        [TestCleanup]
        public void Cleanup()
        {
        }
    }
}

Máme všetko pripravené na pridávanie samotných testov.

V budúcej lekcii, Testovanie v C# .NET - Dokončenie unit testov, pokryjeme triedu unit testy, vysvetlíme si metódy na triede Assert a naučíme sa testovať výnimky. Spomenieme best practices.


 

Mal si s čímkoľvek problém? Zdrojový kód vzorovej aplikácie je k stiahnutiu každých pár lekcií. Zatiaľ pokračuj ďalej, a potom si svoju aplikáciu porovnaj so vzorom a ľahko opráv.

Predchádzajúci článok
Testovanie v C# .NET - Úvod do testovania
Všetky články v sekcii
Testovanie v C# .NET
Preskočiť článok
(neodporúčame)
Testovanie v C# .NET - Dokončenie unit testov
Č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