Testy jednostkowe z Net Core (C#) - Gruca Dariusz

Kup ebooka

10.10 zł
8.38 zł (8,59 zł najniższa cena z 30 dni)

-
Proszę czekać

III. Standard

Testy automatyczne stają się standardem. Coraz więcej firm wymaga tej wiedzy od swoich pracowników. Nie pozostań w tyle. Wiedza oraz umiejętności związane z tą tematyką z pewnością powinny być integralną częścią warsztatu każdego profesjonalnego programisty.

Coraz częściej w ogłoszeniach o pracę widnieje zapis o doświadczeniu komercyjnym, związanym z pisaniem testów automatycznych. Sięgając po tę książkę, zakładam, że ta tematyka jest dla ciebie nowa.

Jeżeli pracujesz jako programista, masz szczęście, możesz jeszcze dziś zacząć zdobywać doświadczenie komercyjne, pisząc testy jednostkowe w swoim obecnym projekcie. Teoria jest potrzebna, ale doświadczenie jest cenniejsze. Dlatego jak najszybciej wykorzystaj przekazaną przeze mnie wiedzę. Twoje pierwsze testy z pewnością nie będą idealne, nie martw się tym, z czasem będziesz w tym coraz lepszy. Ważne jest, abyś rozpoczął tworzenie u siebie nowego nawyku.

W przypadku, gdy dopiero poszukujesz swojej pierwszej pracy, możesz zabłysnąć dodatkową umiejętnością. Stwórz popisowy projekt, w którym pochwalisz się swoją wiedzą. Koniecznie napisz do niego testy. Zamieść projekt na https://github.com/ lub innym portalu, znajdź seniora (np. na https://www.linkedin.com/), daj mu swój projekt do code review, zastosuj się do jego sugestii i popraw swój kod. Pamiętaj, że projekt nie musi być kompletny. Chodzi o to, byś zaprezentował swoje umiejętności związane z tym, jak piszesz, jakiego używasz nazewnictwa, jak tworzysz architekturę rozwiązania. Wpisz link do swojego CV. Popisowy projekt z testami znacząco zwiększy twoją szansę na przejście do kolejnego etapu rekrutacji. Uwierz, niewiele osób ma takie projekty, a programista najlepiej ocenia innego programistę po jego kodzie. Tylko bądź uczciwy, code review to nie to samo co zlecenie komuś takiego projektu. Oszustwo bardzo szybko wyjdzie podczas rozmowy rekrutacyjnej, naprawdę nie warto kombinować. Świat jest mały i swoim nieczystym zagraniem możesz znacząco utrudnić sobie znalezienie pracy w tym zawodzie.

IV. Praca bez testów automatycznych

Wyobraź sobie następującą sytuację. Janusz uważa się za profesjonalnego programistę, przez co testuje swoje rozwiązanie przed wrzuceniem kodu do repozytorium, jednak testy wykonuje manualnie. Dostał on nowe zadanie do wykonania. Z uśmiechem na twarzy napisał piękny i czysty kod. Teraz chce sprawdzić, jak on działa. Uruchamia więc system, loguje się, wybiera z menu interesujący go moduł, następnie wprowadza dane, po czym okazuje się, że pojawił się błąd. Janusz poprawia więc kod i ponawia próbę. Tym razem problemem są dane, których nie może wykorzystać ponownie. Na twarzy Janusza pojawia się grymas, ponieważ musi teraz poświęcić czas, aby je wygenerować. Po kilku minutach klikania po różnych modułach zaczyna pojawiać się u niego frustracja. Kawa, na którą Janusz miał się udać po wykonaniu zadania będzie zbędna. Gdy w końcu udało mu się przygotować wszystko, co potrzeba, sprawdza swoją funkcjonalność. Na ekranie widzi błąd. Poprawia kod i proces zaczyna się na nowo. Horror. Czas nieubłaganie brnie do przodu. Nerwy przebijają się przez tamę, jego stoickiego spokoju zalewając go negatywnymi emocjami. Po siódmej porażce siarczyście klnie pod nosem. Musi wyjść na papierosa, by się uspokoić. Pali od niedawna, biedak nie potrafił w inny sposób poradzić sobie z szarpiącymi go nerwami. W pięknym ogrodzie jego umysłu niczym chwasty zaczynają pojawiać się myśli o zrzuceniu odpowiedzialności za jakość swojej pracy na testera manualnego.

Mija miesiąc, Janusz ma już dość i postanawia porzucić sprawdzanie jakości własnej pracy. Bez testowania, wrzuca zmiany do repozytorium. Z uśmiechem na twarzy przechodzi do tworzenia kolejnych zadań. Czuje złudną ulgę. Wydaje mu się, że niesamowicie przyśpieszył.

Niestety jego pozorne szczęście trwało tylko chwilę. Gdy pewny siebie był w połowie tworzenia nowego modułu, przyszedł czas zapłaty za dług, który niewątpliwie zaciągnął. Tester zaczął zgłaszać błędy do kodu, od którego rozpoczął swoje radosne kodowanie bez sprawdzania jakości. Janusz wrócił do poprzedniego zadania. Poprawa błędu okazała się prosta. Jednak przez to, że pętla zwrotna informacji o problemie występującym w kodzie wydłużyła się, poświęcił on dużo czasu na przypomnienie sobie, o co chodziło w danej funkcjonalności. Z dnia na dzień coraz więcej czasu poświęcał na analizę i poprawę błędów niż na tworzenie nowych funkcji systemu.

Tempo pracy zwolniło, było jeszcze gorzej niż podczas ręcznego sprawdzania kodu. Powrót do kontekstu poprzednich zadań zabierał Januszowi dużo czasu, co go frustrowało. Każdy zgłoszony błąd wpływał negatywnie na jego samopoczucie. Kiedyś czuł się profesjonalistą, teraz za każdym razem, gdy widział, zbliżającego się do jego biurka testera klął pod nosem. Stracił ten błysk w oku i zapał do pracy. Do pokonania negatywnych emocji nie wystarczały mu już papierosy. Co następnie, alkohol?

A mógł tego wszystkiego uniknąć, wystarczyło, żeby nauczył się pisać testy jednostkowe.

V. Poziomy pętli informacji zwrotnej

Człowiek popełnia błędy. Dlatego w pracę programisty wpleciona jest pętla informacji zwrotnej - im jest krótsza, tym lepiej. Szybka informacja o występującym błędzie w algorytmie pozwala zaoszczędzić dużo czasu.

Pętle informacji zwrotnej można podzielić na następujące poziomy:

Poziom 0 - Testy jednostkowe

Poziom 1 - Testy automatyczne serwer buildów

Poziom 2 - Testy manualne, wykonywane przez programistę

Poziom 3 - Testy manualne, wykonywane przez testera

Poziom 4 - Produkcja

Rys. 1. Pętla informacji zwrotnej w stosunku do czasu.

Im więcej błędów zostanie wyeliminowanych, przez testy jednostkowe tym lepiej. Produkcja nie powinna zgłaszać żadnych problemów, jedynie sugestie zmian funkcjonalności. Zdaję sobie sprawę, że tak nie będzie, ponieważ w każdym systemie zdarzają się bugi. Twoim celem jest, by było ich jak najmniej.

Jeśli posiadasz zestawy przypadków testowych dla całego systemu i używasz ich w procesie Continuous Integration, to jako programista możesz zrezygnować z testów manualnych, zostawiając tę czynność testerowi. Jednak zawsze mile widziane jest choć podstawowe "przeklikanie" funkcjonalności

Niektóre z firm tak bardzo ufają testom automatycznym, że pomijają pracę testera i każdy commit po przejściu testów wbudowanych w build serwer, od razu jest wgrywany na produkcję. Ten proces nazywa się Continuous Delivery

IX. Rodzaje testów automatycznych

Testy automatyczne przez pewien okres cieszyły się złą sławą. Wynikała ona ze skupienia się na testach e2e (end-to-end). Przechodzą one przez wszystkie komponenty systemu, począwszy od interfejsu użytkownika, poprzez logikę aplikacyjną a na zapisie w bazie danych kończąc. Ich największym problemem jest kruchość oraz koszt.

Dopiero gdy Mike Cohn w swojej książce Succeeding with Agile zaczął promować piramidę testów automatycznych, wróciły one do łask, stając się teraz wręcz standardem.

Piramida została podzielona na trzy typy.

Rys. 1. Piramida automatyzacji testów

Testy rozłożone w takich proporcjach minimalizują koszt związany z ich wytworzeniem oraz utrzymaniem. Podział ten nie jest uzależniony od architektury systemu czy metodyki jego projektowania, przez co jest uniwersalny. Przyjrzyjmy się każdemu z poziomów:

UI

Testów e2e powinno być jak najmniej, najlepiej kilka na moduł. Ich zadaniem jest sprawdzenie głównej i najważniejszej ścieżki procesu. Są one bardzo kruche oraz wykonują się długo. Chronią przede wszystkim przed regresją kodu.

Service

Testy integracyjne wykonują się nieco szybciej od testów e2e. Jednak nie dostatecznie szybko by stanowiły podstawę piramidy. Ich głównym celem jest testowanie aplikacji w oderwaniu od interfejsu użytkownika. Wykorzystują one jednak rzeczywiste zależności infrastrukturalne, na przykład bazę danych.

Istnieje możliwość wymiany "prawdziwej" bazy danych na tworzoną w pamięci. To rozwiązanie jest znacznie szybsze, ale nie jest pozbawione wad, jedną z nich są różnice między tymi typami baz, przez co nie zawsze da się wprowadzić takie rozwiązanie.

Innym problemem jest to, że te testy i tak wykonują się wolniej niż testy jednostkowe. Przez to w dużych systemach informatycznych opieranie się na testach integracyjnych może spowodować wydłużenie się czasu budowania paczki przez serwer buildów do nieakceptowalnych wartości.

Unit

Testy jednostkowe to podstawa piramidy. Są niezaprzeczalnie najszybsze, jest je też najprościej napisać i wdrożyć do procesu budowania paczki. Wynika to z faktu, że działają w całkowitym oderwaniu od interfejsu użytkownika i od infrastruktury.

Testy jednostkowe będą głównym tematem tej książki. Za ich pomocą sprawdzisz poprawność logiki domenowej oraz aplikacyjnej. W twoim systemie będzie ich najwięcej, więc musisz się skupić na tym, by były one napisane jak najlepiej. Dodatkowo wiedzę oraz doświadczenie zdobyte podczas ich tworzenia możesz przenieść na kolejne poziomy piramidy.

XII. Budowa testu

Bardzo popularną koncepcją budowy testu jest AAA, czyli Arrange-Act-Assert znana również jako Given-When-Then. Opracował tę koncepcję Bill Wake.

Zakłada ona podział struktury testu w następujący sposób:

Arrange w tej części tworzymy założenia wstępne naszego testu. Inicjalizujemy niezbędne obiekty. Obiekt klasy, którą testujemy, nazywamy sut lub cut od (system under test lub class under test). Taki styl nazewnictwa pomaga odróżnić obiekt testowanej klasy, od obiektów, które tylko biorą udział w teście.

Act uruchamia testowaną funkcjonalność. Jeżeli jest taka potrzeba, to przekazujemy wymagane parametry do testowanej przez nas metody.

Assert odpowiada za sprawdzenie warunków określających powodzenie testu.

Rys. 1. Podział testu na części Arrange-Act-Assert.

Sekcje testu polecam oddzielać od siebie enterem. W przypadku testów zawierających dużą ilość kodu dobrze jest napisać komentarz z nazwą sekcji. Znacząco poprawi to czytelność metody testowej.

Rys. 2. Zastosowanie komentarzy do zwiększenia czytelność metody testowej.

Rozdzielenie testu na te trzy sekcje sprawi, że zrozumienie i utrzymanie metod testowych stanie się o wiele prostsze.

XIII. Testy a architektura systemu

Wiesz już jak tworzyć i nazywać zestawy przypadków testowych. Pokażę ci teraz, w jakim miejscu w solucji powinny się one znaleźć.

Zestawy przypadków testowych umieszczamy w osobnym projekcie. W zależności od potrzeb możemy mieć kilka projektów z testami. Najczęściej nazwy projektów zawierają typy testów automatycznych.

Rys. 2. Przykładowa struktura solucji.

Na rysunku powyżej widzisz przykładową strukturę aplikacji podzieloną na komponenty, które znajdują się w osobnych folderach.

W projekcie zawierającym testy stwórz katalogi według struktury solucji. W ten sposób szybko odnajdziesz interesujące cię pliki z testami.

Rys. 3. Przykładowa struktura projektu z testami.

Utrzymanie porządku jest naprawdę istotne. Szczególnie nabiera to znaczenia przy zwiększającej się ilości testów. Dzięki odpowiedniemu nazewnictwu zestawów przypadków testowych oraz umieszczaniu plików we właściwych katalogach w solucji jesteś w stanie bardzo szybko odnaleźć interesujący cię plik.

Bardzo dobrze do tego celu sprawdza się Search Solution Explorer. Wystarczy, że wpiszesz nazwę interesującego cię zestawu przypadków testowych, a Visual Studio go odnajdzie.

Rys. 4. Szybkie wyszukanie zestawu przypadków testowych.

Stosowanie konwencji nazewniczej znacząco uprości ci życie. Zawsze będziesz wiedział w jakim projekcie, katalogu, pliku szukać testów interesującej cię klasy.

XIV. Nazwa testu ma znaczenie

W przypadku gdy test nie przejdzie, to jego dobrze dobrana nazwa powinna spowodować pojawienie się u ciebie zrozumienia potencjalnego powodu wystąpienia usterki. Jeśli bez debugowania jesteś w stanie stwierdzić, co się zepsuło, to wtedy dobrze nazwałeś test. W przypadku, kiedy musisz najpierw zrozumieć, jak test działa, a potem i tak debugować kod, to coś poszło nie tak i nazwa nie spełniła swej funkcji, przez co na niewiele się zdała. Dobre nazewnictwo ułatwia proces refaktoryzacji, wiesz, jaką cześć funkcjonalności przez przypadek uszkodziłeś.

Inną niewątpliwą zaletą odpowiedniego doboru nazwy jest to, że pomoże ci ona w szybszym powrocie do kontekstu pracy. Gdy przychodzisz rano, wystarczy, że otworzysz plik zawierający zestaw przypadków testowych dla funkcjonalności, nad którą pracujesz. Przeglądasz testy i już wiesz, na czym skończyłeś i co zostało ci jeszcze do zrobienia.

Długie nazwy typu: Given_WarunkiWstepne_When_CoSprawdzamy_Then_OczekiwanyRezultat dobrze sprawdzają się przy testach integracyjnych. Krótkie nazewnictwo częściej stosuje się przy testach jednostkowych.

Spotkałem się z różnymi stylami nazewnictwa testów. Oto niektóre z nich:

- CoSprawdzamy_OczekiwanyRezultat

- NazwaMetody_CoSprawdzamy_OczekiwanyRezultat

- NazwaMetody_OczekiwanyRezultat_CoSprawdzamy

- Should_OczekiwanyRezultat_When_CoSprawdzamy FunkcjaDoPrzetestowania (np. IsNotAccesDeniedIfAgeOfUserIsLessThan18)

- When_CoSprawdzamy_Then_NazwaMetody_Should_OczekiwanyRezultat

- When_CoSprawdzamy_Expect_OczekiwanyRezultat

- Given_WarunkiWstepne_When_CoSprawdzamy_Then_OczekiwanyRezultat

Oczywiście nie musisz używać "_":

- CoSprawdzamyOczekiwanyRezultat

- NazwaMetodyCoSprawdzamyOczekiwanyRezultat

- NazwaMetodyOczekiwanyRezultatCoSprawdzamy

- ShouldOczekiwanyRezultatWhenCoSprawdzamy

- WhenCoSprawdzamyThenNazwaMetodyShouldOczekiwanyRezultat

- WhenCoSprawdzamyExpectOczekiwanyRezultat

- GivenWarunkiWstepneWhenCoSprawdzamyThenOczekwianyRezultat

Jest w czym wybierać. Nie ograniczaj się tylko do wymienionych przeze mnie sposobów. Bez problemu możesz wymyślić coś swojego. Kreatywność to coś wspaniałego. Tylko pamiętaj, że nazwa testu ma ci sugerować zakres jego odpowiedzialności.

Więc jaki styl nazewnictwa wybrać?

Najlepiej omówić to z całym zespołem. Ustalcie wspólnie jakie nazewnictwo będzie obowiązywało w całym systemie. Jeżeli zespół nie jest jeszcze doświadczony, to polecam trochę poeksperymentować i po pewnym czasie określić, który styl lub style najbardziej wam odpowiadają.

XVII. Projekt z testami

Projekt z testami można stworzyć na kilka sposobów. Możesz dodać ClassLibrary (.Net Core) i do niego wgrać odpowiednie frameworki za pomocą Manage NuGet Packages lub wpisać odpowiednie komendy służące do instalacji frameworków w oknie Package Manager Console. Jednak niezaprzeczalnie najszybszym sposobem jest dodanie nowego dedykowanego dla testów projektu za pomocą Visual Studio 2019.

Twój czas się dla mnie liczy, więc wybierzmy najszybszą możliwość. Kliknij prawym przyciskiem myszy na nazwę solucji w Solution Explorer i z menu kontekstowego wybierz Add => New Project...

Rys. 1. Menu kontekstowe dla solucji.

Na ekranie pojawi się znajome okno dodawania projektu.

Rys. 2. Okno dodawania projektu w Visual Studio 2019

W wyszukiwarce ulokowanej w górnej części okna wpisz: xunit. Następnie wybierz xUnit Test Project(.Net Core) i kliknij Next.

Rys. 3. Okno ustawień projektu w Visual Studio 2019

W oknie ustawień dla projektu wpisz w pole Project name, nazwę projektu. Ze względu na to, że w tej książce skupiamy się na testach jednostkowych, dlatego odpowiednią nazwą będzie Tests. Unit

Rys. 4. Okno Solution Explorer. Struktura naszej Solucji

Proste. Visual Studio stworzył dla nas projekt z wymaganymi frameworkiami:

Microsoft.Net.Test.Sdk

Xunit

Xunit.runner.visualstudio

Już prawie mamy wszystkie wymagane narzędzia. Pozostało nam zainstalować tylko Moq i przejdziemy do tworzenia pierwszego testu. Zdaję sobie sprawę, że te rozdziały mogą być trochę nudne, ale są niezbędne. Musisz mieć wszystkie potrzebne narzędzia do pracy.

XVIII. Instalacja Moq

Moq wykona za ciebie ogrom pracy poprzez stworzenie obiektów atrap. Pozwoli ci je ustawić oraz weryfikować poprawność integracji między obiektami. Nie musisz już sam pisać klas wykonujących tę pracę, przez co zaoszczędzasz naprawdę dużo czasu i nerwów. Uwierz lub sam poeksperymentuj.

Początek nauki jest ekscytujący, zdobywasz nową wiedzę, chcesz ją jak najszybciej wykorzystać, wszystko widzisz w jasnych barwach. Jednak gdy pozytywne emocje opadną i przyjdzie codzienna praca, w której pisanie testów jednostkowych będzie dla ciebie normą, to docenisz każdą możliwą automatyzację powtarzającego się kodu. Tworzenie ręcznie kodu atrap nie pomaga ci bezpośrednio w osiągnięcie celu, jakim jest sprawdzenie logiki biznesowej lub integracji między obiektami. Bardzo dobrze, że powstały takie narzędzia, które wyręczają nas w tym niewątpliwie nużącym obowiązku.

Zdaje sobie sprawę z tego, że możesz czuć się jeszcze trochę zagubiony i nie do końca rozumieć, o czym piszę. Nic się nie martw, do wszystkiego dojdziemy w odpowiednim momencie. Skupmy się teraz na zainstalowaniu Moq.

Za pomocą Manage NuGet Packages, instalujemy Moq, tak jak by był zwykłym programem. Dlatego zdecydowałem się na tę opcję instalacji, wydaje się bardziej naturalna. Kliknij prawym przyciskiem myszy w projekcie Tests. Unit na Dependencies. Z menu kontekstowego wybierz Manage NuGet Packages...

Rys. 1. Menu kontekstowe dla Dependencies projektu Tests. Unit

Pojawi się okno NuGet Package Manager. W górnej części wybierz zakładkę Browse. Następnie poniżej w wyszukiwarce wpisz: moq. Kliknij lewym przyciskiem myszy na Moq by Daniel Cazzulino.

Rys. 2. Okno NuGet Package Manager

Po prawej stronie okna znajduje się wybór wersji narzędzia. Wybierz Last stable. Następnie kliknij przycisk Install.

Visual Studio 2019 wyświetli okno Preview Changes.