Weiter geht es mit meiner Serie zum Thema Unit-Tests mit Angular. Dieses Mal geht es darum, einen Unit-Test für eine GUI-Komponente zu schreiben. Wie immer findet ihr den vollständigen Quellcode auf GitHub.
Der Umfang des Projektes ist schnell erklärt. Die Anwendung zeigt eine Komponente „TodoComponent" an. Diese lädt über den „TodoService" ein JSON-Objekt. Dieses Objekt beinhaltet die Einträge einer Aufgabenliste die dann angezeigt werden. Die Herausforderung ist nun herauszufinden, wie diese Komponente getestet werden kann. Denn während unseres Unit-Tests möchten wir keine REST-Schnittstelle ansprechen, die uns benötigte Daten zur Verfügung stellt. Wäre diese Schnittstelle - aus welchen Gründen auch immer - nicht erreichbar, würde unser Test fehlschlagen. Das wollen wir auf keinen Fall! Von daher müssen wir irgendwie hinbekommen, dass der "TodoService" keinen wirklichen http-Request absendet.
Um den nachfolgenden Service geht es. Dieser stellt die beschriebene Funktionalität zur Verfügung, um Einträge einer Aufgabenliste per REST-Request zu laden.
Der zweite "beforeEach" Block bereitet unsere Komponente für die nachfolgenden Tests vor. Der Fokus dieses Blog-Posts liegt nicht auf diesen Details. Dies kann man in der offiziellen Angular Dokumentation nachlesen.
Nun können wir uns den Unit-Test selbst anschauen. Würde der Test loslaufen, würde aktuell der "TodoService" den http-Request ausführen. Genau das wollen wir nicht! Als erstes holen wir uns die Instanz des Services und definieren Daten, die statt der Antwort des echten http-Requests benutzt werden sollen. Mit der "spyOn" Angabe erreichen wir, dass die originale Implementierung der "getTodos" überschrieben wird und etwas anderes zurückgibt. Und zwar ein Observable der selbstdefinierten Aufgaben ("mockTodos").Als nächstes müssen wir die Komponente dazu bringen die Aufgabenliste zu laden. Der Ladevorgang wird in der "ngOnInit" Funktion gestartet. Mit dem Aufruf von "detectChanges" wird manuell die "Change Detection" gestartet. Ohne diesen Aufruf würden wir keine Änderung des HTMLs sehen. Dies muss man in Unit-Tests immer explizit starten.
Der Benutzer kann auf jedes einzelne Aufgabe klicken. Dabei wird die "clickOnTodo" Funktion ausgeführt und die ID der Aufgabe auf die Konsole geschrieben. Wir überprüfen nun, ob die Funktion mit den richtigen Übergabeparameter aufgerufen wird. Dazu erzeugen wir ein "spy"-Objekt, welches die "clickOnTodo" Funktion überwacht. Danach wird durch den Test auf das zweite Element der Aufgabenliste geklickt. Mit der letzten "expect"-Angabe überprüfen wir das "spy"-Objekt, ob die Funktion wirklich mit dem richtigen "Todo"-Objekt aufgerufen wurde.
Zwei Sachen werden also überprüft.
- Wird die gesamte Aufgabenliste anzeigt.
- Funktioniert ein Klick auf eine einzelne Aufgabe richtig.
Fazit!
Das war's! Wir haben einen Unit-Test für eine Komponente geschrieben. Die Abhängigkeiten zu einen Service macht uns keine Probleme. Wir wissen nun, wie man eine Funktion eines Services überschreiben kann, ohne die originale Funktionalität aufrufen zu müssen. Diese Vorgehensweise bietet uns die Möglichkeit beliebige Abhängigkeiten aufzulösen bzw. zu entfernen. Dadurch wird unser Komponenten-Test übersichtlich und hat keine Abhängigkeiten nach Extern.
Nachfolgend noch der gesamte Inhalt der "todo.component.spec.ts" Datei. Außerdem ist das gesamte Beispiel auf GitHub zu finden.