Fluent Assertions – przyjemne asserty w testach

Wprowadzenie

Testy jednostkowe oraz testy integracyjne potrafią bardzo ułatwić wyłapywanie błędów podczas tworzenia aplikacji (w szczególności błędów regresji). Dlatego na ogół bardzo chcemy je dodać do aplikacji, ale niestety często spotykamy się z oporem „góry”, bo pisanie testów zajmuje dużo czasu. Z doświadczenia wiem, że jednym z powodów wydłużania czasu pisania testów są rozbudowane asserty. Dlatego w dzisiejszym wpisie chcę Ci pokazać bibliotekę Fluent Assertions, która umożliwia organizację assertów w bardzo przyjemny sposób.

Testy Logiki Biznesowej

W przykładzie do wpisu przygotowałem dwa proste testy logiki biznesowej. Będziemy testować metodę GetById z klasy ProductLogic.

W swoich projektach staram się, aby klasy logiki zwracały obiekty Result, w których znajduje się informacja, czy operacja się powiodła, czy nie. W przypadku niepowodzenia result jest błędny i właściwość Errors ma informacje o błędzie. Gdy wszystko poszło ok, wtedy we właściwości Value znajdują się poprawne dane. Sama klasa Result wygląda tak:

Powyższy kod, poza dwiema wersjami klasy Result (zwykłą oraz generyczną), zawiera również kilka pomocniczych metod, które wspierają w szybkim tworzeniu obiektów wyniku.

Sama metoda GetById z ProductLogic wygląda tak:

Jest ona bardzo prosta. Za pomocą repozytorium z jakiegoś źródła pobiera dane produktu o przekazanym ID. Gdy repozytorium zwróciło nulla, wtedy oznacza to, że danych nie ma i logiki zwraca błędny wynik z komunikatem. W przeciwnym wypadku wynik jest poprawny i w właściwości Value wyniku znajdować się będą dane produktu.

Interfejs dla repozytorium wygląda tak:

Pierwsze testy

W przykładzie do testów użyłem biblioteki xUnit oraz Moq to tworzenie atrap (w tym przypadku atrapy dla repozytorium).

Pierwsza wersja testów zawiera asserty z xUnita, tak dla porównania z tym co będzie później w Fluent Assertions.

Jak widać testy nie są jakoś bardzo skomplikowane. Klasa zawiera dwa testy. Pierwszy dla poprawnego wyniki, drugi natomiast dla błędnego. W pierwszej kolejności tworzę obiekt logiki biznesowej (za pomocą metody Create, która również tworzy wymaganego mocka repozytorium). Następnie konfiguruje mocka, aby zwracał to co chce w danym teście sprawdzić. Kolejno wywołuje metodę GetById i na końcu znajdują się asserty.

Widać, że w przypadku korzystania z klasy Result liczba linijek kodu dla assertów jest dość spora. W przypadku testu dla błędnego wyniku asserty zajmują więcej miejsca niż pozostały kod. Dlatego warto się zastanowić, czy nie można tego jakoś uprościć, aby kod był dużo bardziej zwarty. Właśnie to zrobimy dalej w tym wpisie 🙂

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 20 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Fluent Assertions

Fluent Assertions (https://fluentassertions.com) jest bibliotekom, która organizuje w trochę inny sposób asserty niż jest to domyślnie zrobione w takich bibliotekach jak xUnit, czy nUnit. Jak sama nazwa mówi, korzystamy z fluent api. Sprowadza się to do tego, że na wartości, którą chcemy sprawdzić wywołujemy metodę Should, która następnie zawiera metody, które możemy użyć do zbudowania assertów.

Najlepiej działanie biblioteki zobaczyć na przykładzie. Poniżej znajdują się te same testy co wyżej. Różnią się one budową assertów.

Nie wiem jak dla Ciebie, ale dla mnie taki zapis jest czytelniejszy niż używanie statycznych metod z klasy Assert. Dodatkowo Fluent Assertions umożliwia łatwe rozszerzanie listy dostępnych metod, przez co pisanie testów jest dużo łatwiejsze. Jest też sporo bibliotek, które rozszerzają dostępne asserty. Na przykład Fluent Assertions MVC (https://github.com/fluentassertions/fluentassertions.mvc) dodaje asserty ułatwiające testowanie między innymi kontrolerów.

Fluent Assertions – własne asserty

Według mnie najfajniejszą rzeczą w Fluent Assertions jest to, że możemy bardzo łatwo dodać własne metody do sprawdzania swoich klas. Dzięki czemu możemy zmniejszyć liczbę kodu dla assertów w powtarzającym się kodzie (tak jak to jest w przykładzie powyżej).

Tym razem zacznę od końca i pokaże, co będę chciał osiągnąć. Poniżej jest kolejna wersja testów. Tym razem wszystkie asserty zamknąłem w jednej linii:

Prawda, że jest dużo lepiej? Szczególnie, że obie metody będą wykorzystywane w wielu miejscach w testach logiki biznesowej.

Ok, ale jak to osiągnąć. W pierwszej kolejności tworzymy klasę, w której dodajemy metody, które posłużą nam do testowania obiektów Result (BeSuccess oraz BeFailure). Klasa wygląda tak:

Klasa dziedziczy po ReferenceTypeAssertions i jako parametry generyczne oczekuje typu, dla którego będziemy pisać asserty (w przykładzie generyczna wersja Result) oraz klasy dziedziczącej po ReferenceTypeAssertions (na ogół jest to nasza nowo dodawana klasa). W konstruktorze przekazujemy obiekt, który będziemy sprawdzali.

Same metody przyjmują parametry jakie są jej potrzebne do assertów. Na przykład metoda BeSuccess oczekuje parametru value, który posłuży jej do sprawdzenia, czy właściwość Value z Result jest taka jaką oczekujemy. Natomiast metody BeFailure oczekują komunikatu oraz dodatkowo nazwy właściwości, aby sprawdzić, czy w kolekcji z błędami są odpowiednie błędy.

Wszystkie metody przyjmują dwa zalecane parametry because oraz becauseArgs, które później możemy użyć do budowania komunikatu błędu.

W samej metodzie korzystamy z statycznej właściwości Assertion z klasy Execute. Za pomocą niej dodajemy kolejne warunki, które chcemy sprawdzić. Dla poszczególnych warunków określamy domyślny komunikat błędu, który zostanie zwrócony, gdy dany warunek nie jest spełniony.

Mając już przygotowaną klasę z metodami, musimy dodać jeszcze metodę Should. Robimy to w dodatkowej klasie w formie extenions method:

Rozszerzamy generyczną wersję klasy Result. W metodzie Should tworzymy instancje wcześniej dodanej klasy ResultAssertions, do konstruktora której przekazujemy obiekt, na którym wywołujemy metodę.

Przykład

Tradycyjnie na githubie (https://github.com/danielplawgo/FluentAssertionTests) znajduje się przykład do tego wpisu, w którym możesz samemu sprawdzić w praktyce działanie biblioteki.

Podsumowanie

Bardzo lubię Fluent Assertions za to w jaki sposób pisze się asserty. Jest to czytelniejsze niż to co jest domyślnie w bibliotekach do testów. Dodatkowo bardzo łatwo dodać własne metody, które później ułatwiają tworzenie testów, bez konieczności pisania wielu linijek assertów. A to wszystko możemy wywołać w ten sam sposób jak wbudowane metody (bo już nowych metod statycznych do klasy Assert z xUnita niestety nie dodamy).

Dodatkowo dostępnych jest już sporo rozszerzeń (takich jak wcześniej wspomniane Fluent Assertions MVC), które rozszerzają możliwości biblioteki.

Pamiętaj, że to co pokazałem w tym wpisie to wierzchołek tego, co udostępnia Fluent Assertions. Po więcej informacji odsyłam do strony biblioteki – https://fluentassertions.com.

1 thought on “Fluent Assertions – przyjemne asserty w testach

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *