Wróć do strony głównej
Angular

ng-mocks – z czym to się je?

Każdy kto pisał testy (niekoniecznie w Angularze) z pewnością wie, że zmockowanie zależności testowanego elementu potrafi niekiedy być zmorą. Zależy to oczywiście od jakości kodu w naszym projekcie czy choćby stopnia jego skomplikowania. Spójrzmy na taki przykład:

(snippet z dokumentacji ng-mocks)

Nadmiar takich zależności może skutecznie opóźnić nam przejście do pisania właściwych przypadków testowych. Dlaczego by w takim razie nie umilić sobie życia, skracając i upraszczając konfigurację modułu testowego?

Przyjrzyjmy się więc bibliotece ng-mocks, która w założeniu ma nam pomóc w wyżej wymienionych obszarach. Wspiera ona Angulara od wersji 5 wzwyż (w nowszych wersjach działa również z Ivy), współpracuje zarówno z jest jak i jasmine – jej kompatybilność jest więc całkiem niezła. 

A więc – czy warto instalować do swojego projektu kolejną bibliotekę? W tym artykule postaram się Wam pomóc znaleźć odpowiedź na to pytanie.


Uwaga odnośnie kodu

Wszystkie snippety napisane są z użyciem architektury SIFERS (Simple Injectable Functions Explicitly Returning State). Aby nie odbiegać od tematu artykułu – tym z Was którzy nie zetknęli się z SIFERS nigdy wcześniej polecam świetny artykuł Moshe Kolodnego


Podejście z użyciem helperów

W swojej najprostszej formie biblioteka ng-mocks udostępnia zestaw funkcji pomocniczych, które ułatwiają korzystanie z Angularowej klasy TestBed. Choć klasa ta nie wydaje się trudna w użyciu, to sprawy komplikują się tym bardziej im bardziej skomplikowane jest nasze drzewko zależności. To skutecznie wydłuża ilość mocków które musimy przygotować, aby pisanie przypadków testowych było w ogóle możliwe – a i tak bardzo prawdopodobne, że następna godzina minie nam pod znakiem czerwonych komunikatów w rodzaju “NullInjectorError: No provider for XXX”.

Zobaczmy więc co dobrego oferuje nam ng-mocks:

MockComponent

Funkcja tworzy mock komponentu podanego typu. Zachowa on oryginalny interfejs (co obejmuje Inputy, Outputy, selektor, wsparcie dla transkluzji i kilka innych właściwości), ale cała jego implementacja będzie pusta.

MockModule

Analogicznie, z tym że tworzy mock wskazanego modułu. Podobnie jak w przypadku komponentu odwzoruje ona interfejs ale implementacja będzie pusta. Dodatkowo zmockuje wszystkie pośrednie zależności importowane przez podany moduł – w większości przypadków nie będziemy w ogóle musieli się nimi interesować.

MockProvider

Funkcja pozwala na zmockowanie providera, akceptuje Serwisy i InjectionTokeny. Pokrywa różne sposoby definiowania providera w standardowy sposób.

MockService, MockDirective i MockPipe

Te funkcje umożliwiają zmockowanie elementów o typach wskazanych w nazwie helpera. Dokładniejsze opisy wraz z przykładami znaleźć można w dokumentacji biblioteki (o tutaj). 

Jak widać przeznaczenie helperów jest dość intuicyjne. Spójrzmy teraz na przykładowy kod poniżej. Nie używa on jeszcze ng-mocks. Cały prezentowany w tym artykule kod można pobrać z mojego githuba, do którego link umieściłem na końcu artykułu.

Zastosujmy opisane helpery do powyższego przykładu. Otrzymamy następujący kod:

Pierwsze co rzuca się w oczy to dużo bardziej przejrzysta składnia. Czy jest to wartość dodana? W bardzo małych projektach różnica będzie czysto akademicka, więc niekoniecznie. Natomiast wraz ze wzrostem skali i skomplikowania może być to już niezłym atutem. Zwłaszcza jeśli będziemy wracać do napisanych przez nas testów po dłuższym czasie lub pracować z testami napisanymi przez innego programistę. A to jeszcze nie koniec.

Użycie MockBuildera

Pozwolę sobie zacytować dokumentację biblioteki – w wolnym tłumaczeniu “MockBuilder to najłatwiejszy sposób na zmockowanie właściwie wszystkiego”. No to jedziemy z tematem.

MockBuilder

Jest to funkcja służąca do mockowania różnych elementów napisana w formie fluent API, tj łańcucha metod o – jakby to opisał Wujek Bob – znaczących nazwach (jeśli nie wiesz kim jest Uncle Bob koniecznie to sprawdź jeszcze dziś wieczorem!). Wracając jednak do omawianej funkcji. Sama z siebie może przyjąć dwa opcjonalne argumenty. Pierwszy określa element który nie powinien zostać zmockowany (może nim być komponent lub InjectionToken), zaś drugi – moduł do zmockowania wraz ze wszystkimi zależnościami. Argumenty te mogą być używane razem lub pojedynczo, można też nie podawać ich w ogóle.

Co więcej, funkcja ta tworzy wygodny i czytelny blok kodu, który w zasadzie wyczerpuje temat przygotowania środowiska i pozwala nam skoncentrować się na pisaniu konkretnych przypadków testowych. Zresztą, niech przemówi kod – spróbujmy przebudować poprzedni fragment:

Jak widać MockBuilder dużą część roboty robi za nas. Sama funkcja jest dobrze opisana w dokumentacji, więc nie będę powielać tych informacji tutaj. Dość powiedzieć, że nawet gdy wszystko inne zawiedzie wciąż można odwołać się do starej, dobrej klasy TestBed:

MockRender

Zgodnie z nazwą funkcja ta służy do renderowania komponentów. Używa wewnątrz angularowej metody TestBed.createComponent, ale co warto zaznaczyć zwraca inny typ od swojego pierwowzoru: MockedComponentFixture<Component>. Dla dociekliwych – cel stosowania takiej pośredniej konstrukcji twórcy biblioteki wyjaśniają tutaj.

MockInstance

Funkcja ta ułatwia przygotowywanie i dostosowanie kształtu instancji danej klasy, co jest szczególnie użyteczne w przypadku tworzenia obiektów typu spies.

Spójrzmy na przykład użycia takiej funkcji:

Zauważmy, że ostatni z argumentów wykorzystać można do łatwego stworzenia obiektu typu spy:

Otrzymujemy więc kolejne narzędzie, które dla programisty może okazać się użyteczne. Po więcej odsyłam do dokumentacji.

ngMocks

Nazwa powyżej to namespace agregujący liczne helpery o różnym zastosowaniu. Aby nie przedłużać artykułu raz jeszcze odeślę Cię do dokumentacji biblioteki.

Korzyści

Spróbujmy podsumować omawianą bibliotekę. Otrzymujemy pakiet funkcji-helperów usprawniających “klasyczne” podejście oparte o klasę TestBed. Oprócz tego mamy do dyspozycji funkcję MockBuilder, która daje bardzo szerokie możliwości i ogranicza użycie TestBed jedynie do szczególnych przypadków. Ograniczyliśmy ilość kodu konfigurującego środowisko i zautomatyzowaliśmy tworzenie mocków, co pozwoli skupić się na kodzie testowym – a więc tym, co powinno być najbardziej istotne. 

Odpowiedź nasuwa się sama i – o ile nasz projekt nie jest naprawdę mały – moja opinia będzie krótka. Warto.

Podsumowanie

Wszystkie prezentowane powyżej przykłady kodu były częścią przykładowej appki, przygotowanej z myślą o tym artykule. Całość kodu można pobrać z mojego githuba, tu pokażę jedynie ciało funkcji setup() – konkretne przypadki testowe są już bowiem identyczne w każdym przypadku.

Bez ng-mocks

Komponent

Pipe

Serwis

Ng-mocks – tylko helpery

Komponent

Pipe

Serwis

Ng-mocks z użyciem MockBuildera

Komponent

Pipe

Serwis

Tu zostawiam Was drodzy Czytelnicy do wyrobienia własnej opinii. Jeżeli nie znacie tej biblioteki zachęcam gorąco do jej pobrania i wypróbowania. Miłego pisania!

O autorze

Łukasz Joorewicz

Z Angularem od lat, choć w pisaniu artykułów dopiero debiutuję. Po godzinach bębniarz w rockowym zespole i coraz częściej zapalony rowerzysta. Czasami nie zaszkodzi wstać od laptopa 😉

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *