Test Driven Development z C#

TDDCzęść NR.1 Czym jest TDD? Czyli Test Driven Development. Jest to styl tworzenie programowania, który polega na tym, że zanim napiszesz kod najpierwsz piszesz do niego testy jednostkowe.

Zanim zaczniesz pisać kodu czy to z TDD, czy bez masz na pewno już pewne stwierdzone wymagania do napisania kodu. 

Z TDD bierzesz te pojedyncze wymagania i zanim napiszesz swój pierwszy kod piszesz TEST, który sprawdza dane wymaganie.

Wymaganiem może być funkcja, która zwróci określony obiekt z określonymi polami. W teście byś sprawdził czy te pola są zwracane dla poprawnego zapytania.

Piszesz test jako pierwszy i wiadomo, że bez poprawnego kodu ten test będzie dawał wynik czerwony. Tak ma być, ponieważ logikę napiszesz potem. Test ma pokazać i pilnować Cię i innych programistów o poprawne działanie danej funkcji i jego danego wymagania.

MoqCzęść NR.2 Wcześniej pokazałem Tobie jak przebiega praca programisty z TDD. Dziś będziemy rozbijać klasy na pojedyncze cegiełki, bo inaczej nie można tego testować, gdy wiele klas. Na razie wszystko wygląda w porządku, bo mamy jedną klasę. Sprawy jednak się skomplikują, gdy będziesz miał więcej klas i zależności pomiędzy nimi. Zobaczymy jak to możemy ugryźć

W poprzednim wpisie stworzyliśmy klasę GameBuyingRequestProcessor, która zajmuje się przyjęciem zakupionej gry. Spełniliśmy już do niej następujące wymagania .

TheoryCzęść NR.3 Pora pisać kolejne testy by pokazać jak nasza aplikacja się rozrasta. Dziś przetestujemy i napiszemy wymóg sprawdzenie dostępności gry przed jej zakupem.

Mam już test i kod do metody zapisu w naszym repozytorium do zakupów gier. Dla następnego przypadku będziemy musieli stworzyć nowe repozytorium z metodą IsGameAvailable.

Pomyślmy też co powinno się stać w każdym przypadku. Jeżeli gra nie jest dostępna lub nie istnieje to oczywiście nie możemy jej kupić. Jeżeli gra jest dostępna wtedy wykonujemy polecenie zapisu naszego zakupu.

Oto diagram, który pokazuje przepływ takiej aplikacji.

MemoryCzęść NR.4 Jak testować warstwę dostępu do danych. Czy już na tym etapie jest na potrzebna baza danych? Oczywiście, że nie? Czy musimy tworzyć jakieś statyczne listy, które taką bazę mają symulować? Nie

Wystarcz skorzystać z frameworka Entity Framework, który pozwala na utworzenie bazy testowej w twojej pamięci RAM. Gdy będziesz na produkcji wtedy podłączysz się do prawdziwej bazy z prawidzywmi danymi. Zrobimy to w projekcie ASP.NET CORE.

Jakie mamy wymagania w warstwie dostępu do danych:

  • Chcemy zwracać wszystkie gry dostępne w bazie
  • Chcemy mieć metodę "IsGameAvailable" do sprawdzania czy dana gra jest dostępna do zakupu
  • Chcemy sprawdzić czy zapisujemy zakup/zamówienie do naszego źródła danych, gdy zrobimy metodę Save
  • Chcemy mieć możliwość wyświetlenia wszystkich zamówień/zakupów zaczynając od najstarszych zamówień.

Zanim przejdziemy do następnego przykładu zróbmy porządek w naszym projekcie.

ControllerCzęść NR.5 Kiedy budujesz aplikację z interfejsem użytkownika, możesz wyraźnie odseparować definicję czystego interfejsu np. HTML od logiki takiego interfejsu np. JavaScript.

Logika interfejsu użytkownika definiuje na przykład, co się stanie, gdy użytkownik kliknie przycisk

Moje doświadczenie programistyczne, które ma już 9 lat mówi mi, że w TDD najlepiej się testuje logikę interfejsu użytkownika. Co prawda możesz napisać testy przy użyciu Selenium i testować czy odpowiedni DIV w HTML jest w odpowiednim kolorze, ale taki poziom testów błaga też o pytanie ich sensu. 

Najlepiej testuje się logikę. Co więcej, warto zaznaczyć, że logikę można testować tylko wtedy gdy mamy jasny podział pomiędzy logiką a definicją interfejsu użytkownika.

IActionResultCzęść NR.6 W tym wpisie pokaże Ci jak sprawdzać i testować zawartość IActionResult. Sprawdzimy widoki, do których nawigujemy i sprawdzimy też, czy zwracamy odpowiedni model w IActionResult.

W poprzednim wpisie zrobiliśmy dwa testy do kontrolera i próbowaliśmy postawić nasz projekt ASP.NET CORE (na razie bez sukcesu, bo nie mamy bazy SQL Server). 

Do skończenia tego przykładowego projektu TDD zostało nam jeszcze parę testów.

A dokładnie dwa. Oto wymagania, które nam zostały:

  • Chcemy zwrócić model błędu, gdy gra nie jest dostępna już do zakupu.
    • Użytkownik później ten model zobaczy na stronie HTML.
  • Chcemy sprawdzić, czy nasz kontroler zwraca poprawny IActionResult w metodzie BuyGame.
    • Jeżeli zakup wykonał się poprawnie, to chcemy odesłać użytkownika do odpowiedniej strony. Gdy zakup się, nie udał, to chcemy go przekierować do strony z błędem.

Chcielibyśmy, aby działanie naszego kontrolera wyglądał tak. Jak widzisz tłumaczymy model na request na samym początku. Potem wysyłamy zapytanie do procesora i robimy przekierowanie na odpowiednią stronę w zależności od tego, czy udało nam się kupić grę.

Co więcej, gdy nam się nie udało kupić gry, powinniśmy otrzymać model z błędem, który później zostanie obsłużony w widoku.

AutoFixtureCzęść NR.7 Jeżeli kiedykolwiek pracowałeś nad dużym projektem, używając TDD, to wiesz jak bardzo ważne, jest pokrycie testów.  Pisanie tych testów wymaga czasu i wysiłku. Będziesz często pracował z bardzo złożonymi instancjami klas. Ich utworzenie może zajmować 80 linijek kodu lub więcej. Zanim więc napiszesz  warunek testowy,  już musisz dużo napisać.

Uzupełnianie obiektów bzdurnymi danymi może zajmować więcej czasu niż MOKOWANIE metody do testowania kodu. 

Zresztą patrz na ten kod, który napisałem, gdy robiłem projekt w stylu TDD.

AntyPatternCzęść NR.8 W tym wpisie omówimy antywzorce TDD oraz jego ograniczenia.

Pokazałem ci przykład pracy w metodyce TDD, używając ASP.NET CORE i Entity Framework. Pokazałem Ci jak z AutoFixture, możesz zautomatyzować pewne czynności w testach. Jeśli czytałeś poprzednie wpisy, to wiesz, jak ważne jest wstrzykiwanie zależności i rozbijanie kodu na interfejsy.

Na początku szybko omówiłem Ci zasadę RED-GREEN-REFACTOR. Chciałem, abyś jak najszybciej mógł wskoczyć w TDD, bez długiego nawijania, o co chodzi. Dziś omówimy, jednak to, co powinno być omówione na samym początku, czyli JAK dobrze pisać testy jednostkowe.

Zapraszam :)

UnitTestCzęść NR.9 W tym wpisie zrobimy porównanie frameworków testów jednostkowych, jakie oferuje Visual Studio w swoich szablonach. Ja korzystałem z XUnit w projekcie TDD, ale Ty być może masz innego faworyta.  Prawda jest taka, że różnica pomiędzy tymi frameworkami jest niewielka. Chodzi w końcu o oznaczanie, co jest testem, a co nie i każdy framework ma na to swój sposób.

Czasami nie wszystkie technologie wspierają najnowszy .NET Framework, ale w momencie tworzenia tego wpisu nie widzę problemu z .NET CORE 3.2.

Kiedyś też nie każdy framework można było uruchomić w Visual Studio bez dodatkowych rozszerzeń. Teraz to nie jest problem.

Wszystkie Kategorie