Testowanie NgRx – jak zacząć?

Wiele osób używa frameworka Angular wraz z biblioteką do zarządzania stanem – NgRx i zapewne w pierwszych dniach pracy z tym narzędziem zastanawiało się, jak i co testować. Tak też było i ze mną :-). Z NgRx pracuję w komercyjnych aplikacjach i w tym wpisie chciałbym się podzelić wiedzą jak rozpocząć przygodę z testowaniem funkcjonalności NgRx. Wykonam przegląd podejść, z których korzystam, mam nadzieję, że będą dla Ciebie pomocne!

PS. W tym wpisie nie znajdziesz informacji dlaczego warto testować swój kod oraz czym są unit testy, pozostawiam to Tobie do zagłębienia we własnym zakresie. Skupię się wyłącznie na testowaniu NgRx z użyciem Jasmine. Artykuł jest przeznaczony dla osób już obytych z pracą w NgRx & Angular i ze znajomością frameworka do testowania Jasmine lub innego.

1. Co testować?

NgRx składa się z 4 głównych filarów:
– akcje
– reducery
– selektory
– efekty

Testuję wszystko z wyjątkiem klas „Action Creators”:

Akcje nie posiadają i nie powinny posiadać swojej logiki, Czy akcja jest tworzona poprawnie, jest już sprawdzane chociażby podczas testów reducerów. Poza tym tworzenie instancji klasy z samym kostruktorem lub tworzenie akcji poprzez Factory function (używanie funkcji do produkcji akcji jest także OK) jest zbyt trywialną logiką do testowania w mojej ocenie i nie widzę powodów, aby pokrywać to testami. Zastanówmy się lepiej jak przetestować inne składowe.

Selektory

Testowanie selektorów jest bardzo istotne, ponieważ:

  • często posiadają złożoną logikę, np. wykonują kalkulacje, filtrują dane ze Store etc.
  • dany selektor jest często używany w wielu miejscach aplikacji np. z combineLatest, withLatestFrom lub mapowaniem na potrzeby klasy komponentu

Testuję wyłącznie selektory z logiką, pomijam testy dla selektorów, które wyłącznie wyciagają dane ze store po kluczu (to już jest otestowane przy okazji testów komponentów).

Przykład:

Powyższy selektor zwraca łączną wartość samochodów w naszym garażu.

Test:

Selektory testuję poprzez sprawdzenie, czy „projector function” selektora:

zwraca to co powinien dla zadanych parameterów. Zatem mokuję minimalny, niezbędny zestaw danych dla paramteru tej funkcji (często korzystam z typu Partial aby nie tworzyć całego obiektu, dopóki nie mam takiej potrzeby):

A następnie wywołuję metodę „projector” na selektorze, przekazuję argument i sprawdzam oczekiwaną zwrotkę:

Bardzo wygodny w tym podejściu jest brak potrzeby mockowania całego Store. Interesuje mnie wyłącznie, czy selektor zwrócił to co powinien dla określonych argumentów. Pamiętaj, aby przetestować wszystkie sceneriusze dla Twojego selektora, jeden test case to często za mało dla bardziej skomplikowanych selektorów!

Reducery

W przypadku reducerów testuję każdy zapis do Store, niezależnie czy jest jakaś logika dla danego przypadku (staraj się tworzyć jak najbardziej trywialne reducery, zastanów się zawsze, czy jakaś skomplikowana logika w reducerze, nie powinna być w innym miejscu). Dodatkowo, reducery to pure functions – więc testowanie to czysta przyjemność :-).

Przykład:

Chcemy teraz przetestować, czy dla akcji STORE_CARS, nastąpi prawidłowa zwrotka nowego stanu.
Test:

Powyższy kod jest dość prosty, nie ma za wiele tutaj do tłumaczenia. Sprawdzam, czy nowy stan zwrócony przez carsReducer dla akcji STORE_CARS, odpowiada oczekiwanemu stanowi.

Efekty

Efekty…tam naczęściej dzieje się najwięcej magii. W najbardziej typowym przypadku, gdzie w efektach wykonuję zapytania Http, testuję czy zostały zwrócone akcje na sukces i niepowodzenie. Jeśli mam efekt, który nie zwraca akcji (@Effect({dispatch: false}), to testuję, czy np. funkcja w operatorze „tap”, została zawołana z odpowiednim paramaterem.

Przykład:

Dla powyższego efektu przetestuję:

  • czy zostanie zwrócona akcja StoreCars gdy carsService.getCars() zakończy się sukcesem (wejdzie do operatora map)
  • czy zostanie zwrócona akcja FetchCarsFailed gdy carsService.getCars() zakończy się errorem (np. status Http 404, wejdzie do operatora catchError)

W przypadku testowania efektów mamy najwięcej pracy, gdyż musimy zamokować nasze wstrzyknięte serwisy i stream akcji (actions$).

Test:

Nową rzeczą może być dla Ciebie linia z następującym kodem:

Powyższą funkcję importujemy z ‚@ngrx/effects/testing’. Jest to tzw. mock test provider dla strumienia actions$, przez który przechodzą wszystkie akcje NgRx, i na które nasłuchujemy w efektach. Owy provider, dostarcza nam nowego observabla dla każdego testu.

Używam również ReplaySubject przypisanego do actions$, aby emitować akcje, które zostają przechwycone przez efekty. Następnie w „subscribe” sprawdzam zwracane przez efekty akcje.

Pamiętaj zawsze o przetestowaniu akcji zwracanej w catchError! Wykorzystaj do tego observabla z errorem:

Istnieje także inne podejście do testowania efektów z użyciem Jasmine Marbles. Przykład znajdziesz w demonstracyjnej aplikacji NgRx:
https://github.com/ngrx/platform/blob/master/projects/example-app/src/app/auth/effects/auth.effects.spec.ts

Podsumowanie

 

Wbrew pozorom, testowanie NgRx jest przyjemne. Czy testować? Tutaj jest tylko jedna odpowiedź – koniecznie!. Stan i przepływ danych z nim zwiazany jest kluczowym elementem w aplikacjach opartych o Angular & NgRx.

Pozostaje nam jeszcze testowanie komponentów świadomych Store, gdzie jest najwięcej zabawy w przygotowaniu dedykowanego modułu testowego. To jest temat na mój kolejny wpis (już w produkcji :)). Miłego testowania!

6 Comments

  1. Pingback: Testowanie NgRx – komponenty – Angular.love

  2. wojtrawi

    Fajny art.
    Dla zainteresowanych dodałbym, że selektory dzielą się na 2 kategorie:
    – getter selectors (wybierają tylko części stanu),
    – derive selectors (zwracają view modele).
    Wspomniałeś o tym, ale bez nazwania tych selektorów.

    Z uwagi na brak czasu na testy w pracy, ja ograniczam się do testowania projector functions w selektorach oraz efektów, w których występują higher-order operators lub operatory ograniczające liczbę notyfikacji (first, take itp). Polecam również korzystanie z jasmine-marbles, dlatego że oprócz testowania można przy okazji dogłębnie zrozumieć działanie niektórych operatorów.

    Jeżeli chodzi o mockowanie zwrotki z backendu polecam ten art:
    https://netbasal.com/testing-observables-in-angular-a2dbbfaf5329

    Cytując: ‚That’s what I call cheating’ 😀

Dodaj komentarz

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