Test Driven Development mit Angular Folge 1-5
Angular TDD Workshop
Diese Angular-TDD-Workshop ist eine Variante von diesem Workshop
Wir verwenden hier aber jasmine
statt Jest
Setup
git clone https://github.com/bodote/tdd-angular.git
git checkout v1.0
oder verwende ng new tdd-angular
und reduziere dann src/app/app.component.html
auf ein Minimum , lösche alles und setzte
<div class="content">
<span> app is running!</span>
</div>
ein.
Aufgabe 1
- füge eine Componente namens “favorite-movies” hinzu ( z.B. mit
ng g c ...
). Die ComponenteFavoriteMoviesComponent
soll eine Headline mit dem Titel “My Favorite Movies” anzeigen. - STOP: noch nicht
FavoriteMoviesComponent
oder das Template ändern, sondern- nur Componente anlegen (ggf. mit leeren Methoden/Properties)
- dann den Test schreiben , bzw den Test in
favorite-movies.component.spec.ts
ergänzen! - dann Lösung implementieren; also erst jetzt das Template
favorite-movies.component.html
ändern bis der Test grün ist.
- Tip: arbeite mit
fixture.debugElement.query(...)
oderfixture.debugElement.queryAll(...)
Lösung zu Aufgabe 1
Aufgabe 2
- Wir planen, dass die
FavoriteMoviesComponent
eine Liste der Movies als property vom typsting[]
bekommen soll. Diese Property soll später mal von außerhalb derFavoriteMoviesComponent
(z.B. mit einem@Input()
-Decorator) gesetzt werden können. Teste, dass diese Liste deiner Liebligsfilme, wenn sie nichtundefined
ist, auch im HTML unterhalb der Headline aus Aufgabe 1 angezeigt wird. - STOP: noch nicht die Lösung in
FavoriteMoviesComponent
oder im Template einbauen, denke nicht mal darüber nach! Sondern:- überlege welche Klasse(n), Methode(n), oder Properties (OHNE Initialisierung!), brauchst du für den Test?
- dann den Test schreiben!
- dann die Implementierung bis Test grün ist
- Tip: wenn
describe
oderit
umbenannt wird infdescribe
oderfit
werden NUR diese ausgeführt und vor allem sieht man auch die gerenderte Ausgabe des jeweiligen TestBeds in Browser!Lösung zu Aufgabe 2
- Link zum Video
- Lösung im Git Repo (Anmerkung zu Lösung:
@Input()
vor derfavoriteMovies
property der KlasseFavoriteMoviesComponent
ist für den Unittest unnötig, geht auch ohne, stört aber auch nicht) - Feedback und Kommentare
Aufgabe 3
- nehmen wir an die
favoriteMovies
sollen alternativ auch durch einenService
asynchron bereit gestellt werden. Erzeuge den (leeren) service mitng g s favorite-movies
. - Die Idee ist: wenn unsere Componente bei ihrer Initialisierung in ihrer Properity
favoriteMovies
kein String Array vorfindet, nur dann soll alterntiv der Service gerufen werden. - Teste, ob der Service richtig von der
FavoriteMoviesComponent
verwendet wird, (d.h. du sollst NICHT den Service selbst testen, nur seine korrekte Verwendung!). Dazu den Service “mocken” und prüfe, ob er richtig aufgerufen und verwendet wird. - Teste, ob unsere
FavoriteMoviesComponent
die aus dem Observable empfangen Daten sich auch “merkt”! - Tip: verwende
spyOn()
- refactoriere ggf. den Code, sodass auch die Test aus den ersten 2 Aufgaben wieder laufen
Lösung zu Aufgabe 3
Aufgabe 4
Vermutlich hast du in Aufgabe 3 in der ngOnInit()
methode auf das Observable
, welches du von FavoriteMovieService
bekommst direkt subscribed, richtig ?
Wenn ja , dann
- ändere den Code, sodass du in
ngOnInit()
nur dasObservable
selbst holst und es als solches inFavoriteMoviesComponent
als property speicherst (Verwende auch die übliche Schreibweise der Property mit dem “$” am Ende). - Laufen die Tests noch alle ?
- Eigentlich müsste jetzt zwar der der neue Test auf Aufgabe 3 noch grün sein, aber der Test aus Aufgabe 2 funktioniert nicht mehr, Jedenfalls , wenn du in der
ngOnInit()
methode direkt auf dasObservable
subscribed hattest und dir nur das Ergebniss<string[]>
der Movies gemerkt hattes. - Du musst also jetzt noch den Test aus Aufgabe 2 Anpassen, und vor allem dein Template ändern, damit es direkt das
Observable<string[]>
statt dem<string[]>
verwendet. TIP: verwende eine sogenannte “async pipe” mit*ngIf = "myobservable$ | async as myArray"
Lösung zu Aufgabe 4
- Link zum Video
- Lösung im Git Repo
Bonusaufgabe
- was passiert, wenn du im gerade erstellten Unittest hier:
const favMovServSpy = spyOn(favoriteMoviesService, 'getFavoriteMovies') .and.returnValue(of(favoriteTestMovies))
eine Verzögerung mit
.pipe(delay(100))
in das Test-Observables einbaust ? :const favMovServSpy = spyOn(favoriteMoviesService, 'getFavoriteMovies') .and.returnValue(of(favoriteTestMovies).pipe(delay(100)))
Hinweis : Ergänze oben im *.spec.ts:
import { delay } from 'rxjs/operators';
Lösung zu Bonus-Aufgabe 4
Aufgabe 5
Was ist aber, wenn das Observable
aus unserem FavoriteMovieService
, das uns die ‘Favorite Movies’ liefern soll, einen Fehler wirft ?
In diesem Fall soll in der Browser-View nach der Überschrift an Stelle der Liste der Filme eine Fehlermeldung angezeigt werden. Die Fehlermeldung soll im HTML mit der CSS-Klasse error
versehen werden (z.B. weil unser Stylesheet einen Text mit dieser error
-Klasse rot anzeigen wird ).
- Wie kannst du das im Test “simulieren” ? Hinweis : verwende wieder einen
spy
- und wie kannst du im Test abtesten ob deine
FavoriteMoviesComponent
den Fehler korrekt verarbeitet und auch in der Browseransicht richtig anzeigt ? - Bonusfrage: Der Messagetext der Observerfehlermeldung soll nicht direkt angezeigt
- Hinweis: das
fixture.detectChanges()
darf jetzt NICHT mehr imbeforeEach()
, sondern erst in den eigentlichen Unit-Tests und dort im//act
-Teil ausgeführt werden.Lösung zu Aufgabe 5
- Link zum Video
- Einfache Lösung zu Aufgabe 5 werden, sondern eine andere, für den User verständlichere Meldung.
- Bessere Lösung zu Aufgabe 5
- Feedback und Kommentare
Feedback und Kommentare:
Bitte gib mir dein Feedback! Das hilft uns sehr das Tutorial und die Videos noch besser zu machen.