Angular to przyjemny framework, czyż nie? 😉 Ale tak jak każde narzędzie, ma niestety też i słabsze strony. Zdecydowanym mankamentem jest chociażby pisanie testów integracyjnych templatki i klasy komponentu. Udostępniony moduł do testowania TestBed jest po prostu nieprzyjemny. Czy możemy stwierdzić śmiało, że tak podstawowe czynności jak chociażby:
- mokowanie zależności komponentu
- dostęp do bogatego zbioru asercji (tylko podstawowe z Jasmine)
- łapanie elementów (fixture.debugElement, brrr!!)
- proste przypadki testowe, np. czy "is button disabled"
Są proste i czytelne? Wg mnie… zdecydowanie nie! I w tym momencie wchodzi cały na biało:
Spectator!
Spectator to całkiem przemyślana abstrakcja na wbudowany angularowy moduł do testowania. Głównym autorem jest Netanel Basal, który również prowadzi angularowego bloga – https://netbasal.com, gorąco polecam! Biblioteka znacząco ułatwia pisanie testów, ich czytelność i utrzymanie.
Główne zalety biblioteki:
- wsparcie dla testowania pipes, komponentów, dyrektyw, serwisów HTTP oraz routingu
- uproszczone łapanie elementów drzewa DOM (tak jak w jQuery ?)
- bogatsze API do uruchamiania eventów (Mouse helpers i Keyboard helpers)
- wygodne testowanie z użyciem HostComponent
- wsparcie dla testowania ng-content
- większa liczba matchers, takich jak chociażby .toBeDisabled() czy .toBeFocused()
- wsparcie dla entry components i component providers
- automokowanie serwisów, wow!
- wsparcie dla JEST
- global queries:
const element = spectator.query('.overlay', { root: true, })
Let's check it!
Porównajmy, jak mogą wyglądać elementy testów w Spectatorze w porównaniu z TestBed Module, na prostych przykładach.
- Stan disabled przycisku:
// SPECTATOR expect(spectator.query('.button')).toBeDisabled(); // TEST BED const button = fixture.debugElement.query(By.css('.button')); expect(button.nativeElement.disabled).toBeTruthy();2. Czy *ngFor wyświetla 2 komponenty w liście:
// SPECTATOR expect('app-tshirt-item').toHaveLength(2); // TEST BED const items = fixture.debugElement.queryAll(By.directive(TshirtItemComponent)); expect(items.length).toBe(2);3. Zamokowanie serwisu jako zależności komponentu:
// SPECTATOR providers: [mockProvider(AuthService)], // mockProvider ze Spectator // TEST BED providers: [ { provide: AuthService, useClass: MockAuthService, // nasza zamockowana klasa }, ]4. Wywołanie "enter' na inpucie:
// SPECTATOR const input = spectator.query('input'); spectator.keyboard.pressEnter(input); // TEST BED const input = fixture.debugElement.query(By.css('input')); input.triggerEventHandler('keydown.enter', {});5. Czy element zawiera text:
// SPECTATOR const title = spectator.query('h1'); expect(title).toContainText('Page title'); // TEST BED const title = fixture.debugElement.query(By.css('h1')); expect(title.nativeElement.textContent).toContain('Page title');Nie da się zaprzeczyć, że w Spectatorze wygląda to znacznie prościej i czytelniej! Dziesiątki przykładów znajdziesz w dokumentacji:
https://github.com/ngneat/spectator
Instalacja
Jeśli przekonałem Cię do Spectatora, to nie pozostaje nic innego, jak dodanie go do projektu wraz z ng-mocks poprzez npm:
npm i -D @netbasal/spectator ng-mocks
Schematics
Warto też dodać, że Spectator posiada własne Schematics. Na przykład w celu wygenerowania pliku z testem dla komponentu, możemy posłużyć się poleceniem:
ng g cs path/component-name
Bonus!
A ja jeśli kodujesz z użyciem Visual Studio Code, to koniecznie dodaj wtyczkę do snippetów:

oraz poćwicz z playground od autorów:
PLAYGROUND
Miłego testowania! 😉