uruchamianie testów

uruchamianie testów

Ten wpis jest częścią serii o testach. Całość znajdziesz pod tym adresem.

Wstęp

Przez kilka ostatnich tygodni, przerobiliśmy kilka dużych i ważnych tematów związanych z testowaniem oprogramowania. Omówiliśmy podstawy, napisaliśmy pierwsze testy, zajęliśmy się tez testami z wykorzystaniem atrap (test doubles) i dobrymi praktykami, za którymi warto podążać. Zostało nam jeszcze kilka ważnych kwestii. Dzisiaj zajmiemy się jedną z nich, a będzie to sam proces uruchamiania testów i możliwości jakie ze sobą niesie. Zaczynamy!

Paczki NuGet potrzebne do uruchomienia testów

Jako, że każdy framework jest pisany przez inną firmę/programistę – zarządzany jest przez własny kod. Co za tym idzie, do uruchomienia testów napisanych przy pomocy konkretnego narzędzia, potrzebujemy odpowiednią paczkę NuGet, która po dołączeniu umożliwi ich uruchomienie. Jak już wspominałem w tym poście, istnieje wiele frameworków do pisania testów, nie znam każdego z nich, jednak jestem przekonany, że znaczna większość wybierze jeden z trzech najbardziej popularnych i to na nich się skupię. Myślę o MSUnit, xUnit oraz NUnit. Do uruchomienia testów potrzebujesz odpowiednio:
• MSUnit – MSTest.TestAdapter
• NUnit – NUnit3TestAdapter (w przypadku paczki NUnit w wersji 3.0.0 i wyższej) lub NUnitTestAdapter (w przypadku starszych wersji)
• xUnit – xunit.runner.console do uruchamiania testów z konsoli oraz xunit.runner.visualstudio do uruchamiania testów w oknie Visual Studio

Gdzie uruchomić testy?

Okej, gdy masz już zainstalowane niezbędne paczki – jesteś gotowy, żeby uruchomić testy. Gdzie i jak to zrobić? Już pomagam. Visual Studio udostępnia funkcjonalność nazwaną Test Expoler. Możesz z niej skorzystać wybierając Menu Test, następnie Windows i Test Explorer.

test runner - otwieranie

Jest to dodatkowe okno programu, które wyświetla wszystkie znalezione testy i umożliwia ich wykonanie w dwóch trybach. Dodatkowo udostępnia podstawowe funkcjonalności wyszukiwania, czy organizowania testów i wyświetla rezultaty ich uruchomienia. Na grafikach poniżej widzisz wygląd testów, które nie zostały uruchomione:

test runner - przed uruchomieniem

oraz testów, które się wykonały:

test runner - po uruchomieniu

Ciekawą opcją jest też możliwość tworzenia playlist z wybranymi testami.

Tryby wykonywania testów

W akapicie powyżej wspomniałem o możliwości wykonywania testów w dwóch trybach. Pierwszy z nich to zwykłe uruchomienie. Visual Studio współpracując z oprogramowaniem do uruchamiania testów, które omówiłem wyżej, uruchamia wybrane przez programistę testy i informuje o rezultatach. To zdecydowanie tryb używany najczęściej – daje szybkie wyniki i w większości przypadków wystarcza. W razie powodzenia testu wyświetlane są informacje o czasie potrzebnym do wykonania. Niepowodzenie natomiast powoduje wyświetlenie informacji o wyjątku oraz wiadomości z nim związanej, które zdecydowały o czerwonym statusie testu.

Zdarza się jednak, że podczas przebiegu testu potrzeba nieco więcej informacji. Bywa, że nie jest do końca jasne, co spowodowało, że test nie został ukończony pomyślnie lub chcemy przeanalizować krok po kroku jak wykonuje się kod. Takie potrzeby spełnia drugi tryb – debugowanie. Działa on w dokładnie taki sam sposób jak uruchamianie, z tą różnicą, że w przypadku dodania breakpointa w kodzie testu, podczas jego przebiegu program wstrzyma jego wykonywanie w tym miejscu. Wtedy uzyskuje się wgląd do stanu wszystkich używanych zmiennych, daje to też możliwość śledzenia kolejnych kroków wykonywanych podczas przebiegu testu. Taki tryb pozwala na dogłębną analizę problemu i ułatwia pracę.

Analiza testów (code coverage)

Zadaniem testów jest weryfikacja działania kodu aplikacji. Niesie to za sobą potrzebę użycia wskaźników, które informowałyby o jakości naszych testów. Pomocny może okazać się okazać się procentowy wskaźnik pokrycia kodu (ang. Code coverage). Visual Studio, niestety tylko w wersji Enterprise, umożliwia obliczenie jaka część kodu pokryto testami. Dodatkowo umożliwia pokolorowanie składni, dzięki czemu oznacza przetestowane i pominięte linie kodu.

Pokrycie kodu może być obliczone w dwóch wariantach: line coverage oraz branch coverage. Pierwszy z nich zlicza przetestowane linie kodu, pomijając komentarze, instrukcje warunkowe, itp., a następnie porównuje je do całkowitej liczby linii w projekcie. W ten sposób liczy się wskaźnik line coverage.

Drugi natomiast oblicza ilość przetestowanych rozgałęzień logiki – głównie na skutek stosowania instrukcji warunkowych.

Przykład:

        public void ExampleMethod(bool condition)
        {
            if (condition)
            {
                var data = GetData();
                Validate(data);
                SendData(data);
            }
            else
            {
                NotifyError();
            }
        }

Opierając się na tym przykładzie, w przypadku gdyby w każdym z testów warunek w instrukcji zawsze zwracał wartość true – przetestowana byłaby tylko jedna część instrukcji, co dałoby line coverage 75% oraz branch coverage 50%, bo nie przetestowaliśmy drugiego przypadku rozgałęzienia logiki.

Obserwując te wskaźniki warto zwracać uwagę na obydwa. Dopiero w połączeniu dają one pogląd na wartość napisanych przez nas testów. Jako dowód zostawiam Cię z jeszcze jednym przykładem:

        public void AnotherExampleMethod(CreditCard creditCard)
        {
            if (creditCard != null)
            {
                Validate(creditCard);                
            }

            SendData(creditCard.CardNumber, creditCard.Owner);
        }

Gdyby testy tej metody analizowały tylko przypadki, w których warunek w instrukcji zwraca wartość true, to line coverage wynosiłoby 100%. Wydaje się super nie? No właśnie nie do końca, bo nadal mamy nieprzetestowany przypadek, w którym warunek w instrukcji zwraca false. W takim scenariuszu wyrzucone zostanie NullReferenceException. Patrząc na branch coverage zauważymy, że wynosi ono 50% – dlatego właśnie warto patrzeć na oba te wskaźniki. Oczywiście przykład ten jest mocno przejaskrawiony i w rzeczywistym kodzie błąd będzie trudniejszy do wychwycenia, co według mnie sprawia, że jeszcze bardziej powinniśmy opierać się obu tych wskaźnikach.

Do jakiego pokrycia dążyć?

Skoro już jesteśmy w temacie code coverage, warto wspomnieć kilka słów o tym do jakich jego wartości powinniśmy dążyć. Odpowiedź oczywiście nie jest tak prosta jak podanie jednej liczby. Nie, idealne code coverage to nie jest 100%, 87,51% ani 53,96%. Warto zwrócić uwagę na kilka faktów. Po pierwsze nie wszystko da się przetestować i nie ma sensu pisać testów np. dla klas odpowiedzialnych z rejestrację komponentów w kontenerze IoC lub klasach wykorzystywanych podczas rozruchu aplikacji. Dla takich klas warto użyć atrybutu [ExcludeFromCodeCoverage], który powoduje pominięcie elementów objętych nim podczas liczenia wskaźnika pokrycia kodu.

Ważną kwestią jest też fakt, że nawet stuprocentowe pokrycie nie jest gwarancją braku błędów w kodzie. Warto o tym pamiętać i nie przeceniać dobrodziejstw jakie niesie za sobą code coverage, a polegać też na własnej analizie.

Napisanie testów, które pokrywają taką metodę byłoby banalne, gdybyśmy jednak w żadnym z nich nie podali wartości null jako parametr, całość zyskałaby pełne pokrycie i niestety nie byłaby odporna na wszystkie błędy.

Moim zdaniem warto dążyć do jak najwyższego pokrycia kodu testami, jednocześnie pamiętając, że nie jest to wymogiem samym w sobie. Warto też nie traktować wskaźnika pokrycia kodu jako wyznacznika jego jakości. Lepszym sposobem będzie uznawanie go za narzędzie, które pozwoli wykryć miejsca w kodzie, wymagające większej uwagi – te nieprzetestowane, lub przetestowane tylko pobieżnie.

Narzędzia do Code Coverage

Dla wszystkich, którzy nie korzystają z Visual Studio w wersji Enterprise mam dobrą wiadomość. Nie musicie rezygnować z analizowania pokrycia kodu. W Internecie można znaleźć wiele rozwiązań udostępnianych na licencji OpenSource, a oferujących takie możliwości. Często oferują wiele dodatkowych funkcjonalności ułatwiających pracę, np. wyświetlanie wyników w wygenerowanym pliku HTML. Dzięki nim analiza kodu staje się prostsza i daje więcej możliwości. Poniżej kilka przykładowych projektów:
• NCover http://www.ncover.com/support/docs/index
• Parasoft https://parasoft.force.com/customerportal/CustomSiteLogin?startURL=%2Fcustomerportal%2FCommunitiesDocuments
• OpenCover https://github.com/OpenCover/opencover

Podsumowanie

W dzisiejszym artykule omówiliśmy wiele ważnych kwestii. Przede wszystkim jak uruchomić testy. Nie jest to skomplikowany proces, jednak z pewnością może przysporzyć problemów osobom, które nie miały z nim jeszcze styczności. Ten artykuł wychodzi im naprzeciw. Dodatkowo zastanowiliśmy się nad analizą testów i ich pokrycia właściwego kodu, a także do jakich wartości tego wskaźnika dążyć. Jeżeli artykuł pomógł Ci w jakiś sposób, to proszę przekaż go dalej. Byłoby super, gdyby mógł pomóc też innym.

Please follow and like us:

Dodaj komentarz

This site uses Akismet to reduce spam. Learn how your comment data is processed.