Wybierz region
pl
  • PL
  • EN
Wydrukuj

Wprowadzenie do testów jednostkowych w Angularze

Testy jednostkowe to najprostsze testy jakie możemy wykonać już po wygenerowaniu aplikacji dzięki Angular CLI. Domyślnie testy jednostkowe w Angularze wykorzystują framework Jasmine oraz środowisko uruchomieniowe Karma, wystarczy jedna komenda (ng test), aby sprawdzić czy wygenerowany domyślny komponent spełnia wymagania testów. Sam Angular dostarcza także kilka funkcjonalności, dzięki którym możemy w pełni przetestować odseparowane części aplikacji.

Stworzyłeś już kilka aplikacji frontendowych, niektóre może opublikowałeś i mają swoje miejsce w Internecie, znasz ich najważniejsze części składowe, wiesz co jest możliwe w przeglądarce, ale zawsze unikałeś testów. Być może unikałeś ich celowo albo nie wiedziałeś jakie jest ich zastosowanie. W końcu, dla początkujących programistów frontend testy na pierwszy rzut oka nie wydają się pierwszą potrzebą. Natomiast testy to bardzo ważna część aplikacji, jeśli nie chcemy cały czas wracać do tych samych błędów, na które poświęciliśmy już i tak wystarczająco wiele czasu.

Aby w pełni zrozumieć, na czym polegają test jednostkowe przyjrzyjmy się, na jakie kategorie dzielimy testy aplikacji frontendowych:

Testy E2E z ang. end-to-end, polegają na kompleksowych testach w wielu różnych przeglądarkach, tak jakby robił to prawdziwy użytkownik. Sprawdzają wykonanie aplikacji od początku do końca w prawdziwych scenariuszach. Dużo czasu zajmuje ich przygotowanie i są najbardziej kosztowne.

Testy integracyjne sprawdzają działanie i interakcję między wieloma częściami aplikacji oraz zasobami zewnętrznymi, np. baza danych/backend. Są szybsze i łatwiejsze do stworzenia.

Testy jednostkowe sprawdzają działanie najmniejszych odizolowanych części aplikacji: działanie metod, elementów UI, poprawne przekazywanie danych wejściowych itd. Są najszybsze i najłatwiejsze do stworzenia.

Wymienione rodzaje testów tworzą ekosystem narzędzi, który usprawnia pracę nad rozwojem aplikacji i pozwoli uniknąć wielu problemów. W praktyce najczęściej stosowanym rodzajem testów są testy jednostkowe, z powodu swojej prostoty oraz pewności, że nasze komponenty osobno działają zgodnie ze swoimi założeniami w większości przypadków.

Narzędzia do testowania w Angularze

Przyjrzymy się działaniu testów jednostkowych w Angularze, który domyślnie wykorzystuje Jasmine jako framework testowy oraz Karma jako środowisko uruchomieniowe dla testów.

Framework Jasmine jest narzędziem niezależnym od Angulara i pozwala na testowanie najmniejszych elementów kodu, nie wymagając dostępu do DOM. Zapewnia przejrzystą składnię, dzięki czemu testy można pisać bardzo szybko i są łatwe do zrozumienia na pierwszy rzut oka. Angular dostarcza dodatkowe funkcjonalności do wykorzystania razem z Jasmine, dzięki czemu testowanie komponentów, serwisów czy dowolnej części składowej aplikacji jest znacznie prostsze.

Karma zapewnia środowisko uruchomieniowe dla naszych testów i przedstawia ich wyniki w konsoli oraz przejrzystym interfejsie przeglądarkowym. Odpowiada za to, jaki silnik przeglądarki przeprowadza testy oraz za ponowne uruchamianie testów, gdy wykryje zmianę w plikach projektu.

Oba narzędzia są domyślnie dostępne i gotowe do użycia w nowo utworzonym projekcie przy użyciu Angular CLI. Konfiguracja narzędzi znajduje się w plikach ` karma.conf.js` oraz `test.ts` w folderze `src/`.

Testy integracyjne i jednostkowe w Angularze znajdują się w tym samym rodzaju plików (.spec.ts). Testy jednostkowe posiadają bliźniacze nazwy z plikami, których dotyczą i znajdują się w tych samych katalogach, natomiast testy integracyjne zazwyczaj znajdują się w osobnym katalogu np. `tests/` w głównym katalogu projektu z racji tego, że zazwyczaj nie przynależą do określonych modułów/komponentów/itp.

Pierwsze testy jednostkowe

Wygenerowanie nowej aplikacji przy pomocy Angular CLI i polecenia `ng new` tworzy przykładowy moduł oraz komponent, jest tam także jeden przykładowy test dotyczący komponentu `app.component.ts`.

Nowy komponent skład się w szablonie tylko diva i spana.

https://gist.github.com/przemekpobuta/ab5f47220655c3de1fcc904536612829

Natomiast komponent zawiera tylko jedną właściwość `title`.

https://gist.github.com/przemekpobuta/b1ec15aa7b1457e042838440dd9e6b64

Wygenerowany plik testów zawiera trzy testy które sprawdzają kolejno:

  • czy instancja komponentu została utworzona
  • czy właściwość `title` ma określoną wartość
  • czy wygenerowany tytuł jest zgodny z oczekiwaniami

W kolejnej części wpisu wyjaśnię elementy składowe poniższego testu.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

Aby wykonać testy użyjemy komendy `ng test` która otworzy nowe okno przeglądarki z podglądem testów.

Jak widać w przejrzysty sposób przedstawiono wynik testu. Klikając w konkretne pozycje można powtórzyć wybrane testy lub ich grupy. W przypadku niepowodzenia któregoś z testów możemy zobaczyć, które testy oraz z jakiego powodu się nie powiodły.

Jest to komenda która wykonuje wszystkie testy jakie znajdzie w projekcie, czyli wszystkie pliki z końcówką `.spec.ts`.

Przebieg testów

Każdy plik testów jednostkowych składa się z funkcji grupy `describe()`, która określa czego będzie dotyczyć grupa testów, natomiast każdy test `it()` definiuje scenariusz testu. Oba wymagają nazwania, grupy można zagnieżdżać, ma to później wpływ na nazwę z jakiej składa się dany test: jest to nazwa testu z przedrostkami grup. Nazwa testu powinna zaczynać się od `should …`.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

Do inicjalizacji zamiennych na potrzeby testów wykorzystuje się funkcję `beforeEach()` która jest wywoływana przed każdym testem.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

W `beforeEach()` przy pomocy `TestBed` tworzymy testowy moduł który umożliwi nam testowanie składowych komponentu `AppComponent`.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

`TestBed` jest główną klasą która umożliwia testowanie elementów składowych aplikacji oraz bibliotek Angular. `TestBed.configureTestingModule` daje nam podobne rezultaty jak uruchomienie aplikacji z zadeklarowanym komponentem przy pomocy `@NgModule`.

Aby mieć dostęp do elementów komponentu musimy utworzyć jego instancję, także przy pomocy `TestBed`, jest to `fixure` czyli `ComponentFixure` który pozwala na interakcję ze stworzoną instancją komponentu.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

`debugElement` jest obiektem, który okrężną drogą, daje nam dostęp do DOM, w tym przypadku instancji komponentu. Okrężną ponieważ zabezpiecza nas przed problemami zależnymi od środowiska, zwracając nam obiekt dostosowany do środowiska na którym dokonujemy testu.

Wreszcie, przy pomocy podstawowej metody `expect()`, weryfikujemy wartość przy pomocy jednej z metod porównujących: `toBeDefined`, `toBeTruthy`, `toContain`, `toEqual`, itp. W tym przypadku sprawdzamy czy instancja komponentu istniej, jeśli tak to test zakończony jest sukcesem.

W ten sam sposób możemy sprawdzić czy wartość zmiennych jest zgodna z testem, ponieważ instancja komponentu daje nam dostęp do jego pól.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

Dzięki obiektowi `nativeElement` mamy dostęp do DOM i przy pomocy `querySelector()` możemy wyszukać odpowiedni element i wydobyć jego zawartość tekstową. W tym przypadku koniecznie było już wykorzystanie funkcji `detectChanges()`która ręcznie aktywuje cykl detekcji zmian dla komponentu. Bez wywołania tej funkcji nie odbędzie się emitowanie danych (data binging) w komponencie.

https://gist.github.com/przemekpobuta/8b5249303a93a84d52342530de7cd144

Podsumowanie

Jak widać tworzenie i zrozumienie testów jednostkowych nie jest trudne, dzięki przejrzystym blokom konstrukcyjnym możemy budować kolejne scenariusze testów, żeby testować każdą część aplikacji w Angularze. Zdobyta wiedza na temat podstaw testowania w Angularze pozwoli nam teraz na samodzielne testowanie prostych komponentów.

Powyższy wpis to tylko niewielka część tego co umożliwiają testy, ponieważ testować można także komponenty z zależnościami, serwisy ze zmockowanymi danymi, itp. Kompleksową wiedzę na temat tego co jest możliwe i przykłady możemy znaleźć w oficjalnej dokumentacji Angulara oraz Jasmine.

 


Przemysław Pobuta

Jako młodszy programista frontend w pionie Energetyki i Gazownictwa, tworzę aplikacje bazujące na frameworku Angular. Interesuje się start-upami, User Experience oraz produktywnością, w wolnym czasie pochłaniam wiedzę w formie audiobooków i podcastów oraz nieudolnie próbuje kolejnych sportów indywidualnych.


Wydrukuj