Modest Programmer logo
Nadszedł najwyższy czas na trochę praktyki. W poprzednich artykułach o testach jednostkowych było dużo teorii, w celu jej utrwalenia przejdziemy dzisiaj do praktyki. Pokaże Ci, jak mogą wyglądać testy jednostkowe w .NET. Przetestujemy dzisiaj słynny algorytm FizzBuzz. Słynny, ponieważ często na rozmowach kwalifikacyjnych rekruterzy proszą o jego zaimplementowanie :)

Jak Pisać Testy Jednostkowe? Przykład Testu Jednostkowego w .NET Dla Początkujących

Co to jest FizzBuzz?
Jest to prosty algorytm, który na podstawie przekazanego argumentu zwraca odpowiednią wartość. Zasady są takie:
-Jeżeli argument jest podzielny przez 3, zwraca Fizz.
-Jeżeli argument jest podzielny przez 5, zwraca Buzz.
-Jeżeli argument jest podzielny przez 3 i przez 5, zwraca FizzBuzz.
-Jeżeli argument nie jest podzielny przez 3 ani przez 5, zwraca wartość argumentu.

Implementacja algorytmu może wyglądać mniej więcej tak:

namespace TestsDotNet
{
    public class FizzBuzz
    {
        public static string GetOutput(int number)
        {
            if ((number % 3 == 0) && (number % 5 == 0))
                return "FizzBuzz";

            if (number % 3 == 0)
                return "Fizz";

            if (number % 5 == 0)
                return "Buzz";

            return number.ToString();
        }
    }
}


Przykład testu jednostkowego w .NET


Nasze testy jednostkowe napiszemy przy użyciu biblioteki NUnit. Zgodnie z krokami, które zostały szczegółowo opisane w poprzednim artykule:
-Dodajemy nowy projekt z testami, będzie to TestsDotNet.UnitTests.
-Do projektu z testami TestsDotNet.UnitTests, dodajemy referencję do projektu testowanego, w tym przypadku TestsDotNet.
-Instalujemy NUnit poprzez NuGeta.
-Instalujemy NUnit3TestAdapter poprzez NuGeta.
-Dodajemy klasę testową o nazwie FizzBuzzTests.

Teraz przez chwilę, zastanówmy się na spokojnie, co konkretnie chcemy przetestować, a następnie zdefiniujmy przypadki testowe, które chcemy przetestować. Tutaj możemy się posłużyć wymaganiami zdefiniowanymi powyżej. Zaczynając od pierwszego - jeżeli argument jest podzielny przez 3, zwraca "Fizz". Stosując odpowiednią konwencję nazewnictwa, to jest MetodaTestowana_Scenariusz_OczekiwaneZachowanie, możemy nazwać naszą metodę GetOutput_WhenInputIsDivisibleOnlyBy3_ShouldReturnFizz. Nie zapomnijmy o oznaczeniu metody atrybutem [Test].

[Test]
public void GetOutput_WhenInputIsDivisibleOnlyBy3_ShouldReturnFizz()
{
}

Piszemy nasz test zgodnie z zasadą AAA - Arrange, Act, Assert. Najpierw w Arrange, przygotujemy nasz obiekt do testów. Chcemy testować metodę klasy FizzBuzz, więc zainicjalizujmy najpierw ten obiekt.

var fizzbuzz = new FizzBuzz();

W Act następuje działanie, czyli uruchamiamy metodę GetOutput z odpowiednim parametrem - według scenariusza i zwracamy wartość do zmiennej na przykład o nazwie result.

var result = fizzbuzz.GetOutput(3);

W Assert weryfikujemy czy rzeczywiście w zmiennej result jest to, czego oczekujemy, czyli "Fizz". Możemy tutaj skorzystać z klasy Assert frameworka NUnit, a konkretnie metody statycznej That. Czyli weryfikujemy, że result czyli to, co zwróciła metoda GetOutput(3) - jest równe "Fizz".

Assert.That(result, Is.EqualTo("Fizz"));

I gotowe, napisaliśmy test do 1 scenariusza :) Nasza pierwsza metoda testująca, będzie wyglądać w ten sposób:

[Test]
public void GetOutput_WhenInputIsDivisibleOnlyBy3_ShouldReturnFizz()
{
    var fizzbuzz = new FizzBuzz();

    var result = fizzbuzz.GetOutput(3);

    Assert.That(result, Is.EqualTo("Fizz"));
}

Następnie piszemy test dla kolejnego scenariusza, tym razem oczekujemy, że dla parametru 5, zostanie zwrócony wynik "Buzz". Nasz test będzie wyglądał bardzo podobnie, zmieniamy tylko argument na 5, oczekiwany wynik w tym przypadku powinien być "Buzz":

[Test]
public void GetOutput_WhenInputIsDivisibleOnlyBy5_ShouldReturnBuzz()
{
    var fizzbuzz = new FizzBuzz();

    var result = fizzbuzz.GetOutput(5);

    Assert.That(result, Is.EqualTo("Buzz"));
}

Musimy jeszcze zweryfikować czy dla argumentu podzielnego przez 3 i 5 zostanie zwrócone "FizzBuzz", ale to już wiesz jak zrobić.

[Test]
public void GetOutput_WhenInputIsDivisibleBy3And5_ShouldReturnFizzBuzz()
{
    var fizzbuzz = new FizzBuzz();

    var result = fizzbuzz.GetOutput(15);

    Assert.That(result, Is.EqualTo("FizzBuzz"));
}

A także, czy dla argumentu nie podzielnego przez 3 ani przez 5, zostanie zwrócony ten sam argument. Tutaj nie ma co szukać jakichś magicznych liczb, jako argument możemy podstawić 1, czyli liczbę, która nie jest podzielna przez 3 ani przez 5. Jeżeli podstawimy jakąś liczbę typu 679, to w przyszłości jak wrócimy do tego testu, to będziemy tracić czas i zastanawiać się co akurat taka liczba mogła w tym przypadku oznaczać :) Czy to jakaś szczególna liczba, czy po prostu losowa, która nie jest podzielna przez 3 ani przez 5.

[Test]
public void GetOutput_WhenInputIsNotDivisibleBy3Or5_ShouldReturnInput()
{
    var fizzbuzz = new FizzBuzz();

    var result = fizzbuzz.GetOutput(1);

    Assert.That(result, Is.EqualTo("1"));
}

Uruchamiamy nasze testy i w test explorze powinien się pojawić zielony kolor, oznaczający, że nasze testy przeszły pozytywnie, nasz kod produkcyjny działa prawidłowo - wdrażamy na produkcję :)


Klasa FizzBuzzTests:


using NUnit.Framework;

namespace TestsDotNet.UnitTests
{
    public class FizzBuzzTests
    {
        [Test]
        public void GetOutput_WhenInputIsDivisibleOnlyBy3_ShouldReturnFizz()
        {
            var fizzbuzz = new FizzBuzz();

            var result = fizzbuzz.GetOutput(3);

            Assert.That(result, Is.EqualTo("Fizz"));
        }

        [Test]
        public void GetOutput_WhenInputIsDivisibleOnlyBy5_ShouldReturnBuzz()
        {
            var fizzbuzz = new FizzBuzz();

            var result = fizzbuzz.GetOutput(5);

            Assert.That(result, Is.EqualTo("Buzz"));
        }

        [Test]
        public void GetOutput_WhenInputIsDivisibleBy3And5_ShouldReturnFizzBuzz()
        {
            var fizzbuzz = new FizzBuzz();

            var result = fizzbuzz.GetOutput(15);

            
            Assert.That(result, Is.EqualTo("FizzBuzz"));
        }

        [Test]
        public void GetOutput_WhenInputIsNotDivisibleBy3Or5_ShouldReturnInput()
        {
            var fizzbuzz = new FizzBuzz();

            var result = fizzbuzz.GetOutput(1);

            Assert.That(result, Is.EqualTo("1"));
        }
    }
}

Wynik w test explorer:

FizzBuzz - wynik testów


PODSUMOWANIE


W dzisiejszym artykule skupiliśmy się na praktyce, pokazałem Ci, jak wygląda cały proces testowania pojedynczej metody. W przeciwieństwie do przykładu z poprzedniego artykułu dzisiaj przeanalizowaliśmy różne przypadki testowe i najczęściej właśnie tak to wygląda w naszych aplikacjach. Rozpisaliśmy konkretne przypadki testowe, czyli scenariusze, które musimy sprawdzić, a następnie napisaliśmy do nich testy. Wszystkie nasze testy przeszły pozytywnie, dzięki temu wiemy, że nasz program działa według założeń. Mając już napisane testy, jeżeli w przyszłości okaże się, że coś trzeba zmienić w naszej metodzie, zrefaktoryzować kod, lub dodać nową funkcjonalność - mamy pewność, że nie spowoduje to żadnej regresji, bo wówczas zostanie ona wykryta przez testy jednostkowe. Ten przykład był o tyle prosty, że nie było w nim żadnych zewnętrznych zależności. O tym, jak radzić sobie z zewnętrznymi zależnościami w testach jednostkowych opiszę Ci w kolejnym artykule. Do zobaczenia!

Poprzedni artykuł - 100% Tego, Co Musisz Wiedzieć o Testach Jednostkowych.
Następny artykuł - Jak Pozbywać się Zewnętrznych Zależności w Testach Jednostkowych? Wprowadzenie do Mockowania Danych w C#.
Autor artykułu:
Kazimierz Szpin
Kazimierz Szpin
Programista C#/.NET. Głównie pisze aplikacje w ASP.NET MVC, WPF oraz Windows Forms. Specjalizuje się w testach jednostkowych.
Autor bloga ModestProgrammer.pl
Komentarze (2)
Bartek
BARTEK, 2 marca 2020 17:10
Czemu nie skorzystać z Assert.AreEqual?
Kazimierz Szpin
KAZIMIERZ SZPIN, 2 marca 2020 19:34
@BARTEK, głównie ze względu na czytelność przykładu :) Moim zdaniem, zapis z Assert.That jest bardziej czytelny. Od razu wiadomo co jest wartością oczekiwaną, a co wartością rzeczywistą. Z Assert.AreEqual nie do końca i często te 2 wartości są mylone, przynajmniej gdy dopiero zaczynamy uczyć się testów. A co za tym idzie, jeżeli test nie przechodzi to otrzymujemy złą wiadomość.
Dodaj komentarz

Wyszukiwarka

© Copyright 2020 modestprogrammer.pl. Wszelkie prawa zastrzeżone. Polityka prywatności. Design by Kazimierz Szpin