Advertisement
  1. Code
  2. Angular

Testen von Komponenten in Angular mit Jasmine: Teil 2, Dienste

by
Read Time:20 minsLanguages:
This post is part of a series called Testing Components in Angular Using Jasmine.
Testing Components in Angular Using Jasmine: Part 1

German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Dies ist der zweite Teil der Serie zum Testen in Angular mit Jasmine. Im ersten Teil des Tutorials haben wir grundlegende Komponententests für die Pastebin-Klasse und die Pastebin-Komponente geschrieben. Die Tests, die anfänglich fehlschlugen, wurden später grün gemacht.

Überblick

Hier ist eine Übersicht darüber, woran wir im zweiten Teil des Tutorials arbeiten werden.

High level overview of things weve discussed in the previous tutorial and things we will be discussing in this tutorialHigh level overview of things weve discussed in the previous tutorial and things we will be discussing in this tutorialHigh level overview of things weve discussed in the previous tutorial and things we will be discussing in this tutorial

In diesem Tutorial werden wir:

  • neue Komponenten erstellen und weitere Unit-Tests schreiben 
  • Tests für die Benutzeroberfläche der Komponente schreiben
  • Unit-Tests für den Pastebin-Dienst schreiben 
  • eine Komponente mit Ein- und Ausgängen testen 
  • eine Komponente mit Routen testen 

Lassen Sie uns anfangen!

Hinzufügen einer Paste (Fortsetzung)

Wir waren in der Mitte des Prozesses des Schreibens von Komponententests für die AddPaste-Komponente. Hier haben wir im ersten Teil der Serie aufgehört.

Wie bereits erwähnt, werden wir keine strengen UI-Tests schreiben. Stattdessen werden wir einige grundlegende Tests für die Benutzeroberfläche schreiben und nach Möglichkeiten suchen, die Logik der Komponente zu testen.

Die Klickaktion wird mit der DebugElement.triggerEventHandler()-Methode ausgelöst, die Teil der Angular-Testdienstprogramme ist.

Bei der AddPaste-Komponente geht es im Wesentlichen darum, neue Pasten zu erstellen. Daher sollte die Vorlage der Komponente eine Schaltfläche zum Erstellen einer neuen Einfügung enthalten. Durch Klicken auf die Schaltfläche sollte ein "modales Fenster" mit der ID "source-modal" angezeigt werden, das ansonsten ausgeblendet bleiben sollte. Das modale Fenster wird mit Bootstrap erstellt. Daher finden Sie möglicherweise viele CSS-Klassen in der Vorlage.

Die Vorlage für die Add-Paste-Komponente sollte ungefähr so aussehen:

Der zweite und dritte Test geben keine Auskunft über die Implementierungsdetails der Komponente. Hier ist die überarbeitete Version von add-paste.component.spec.ts.

Die überarbeiteten Tests sind insofern expliziter, als sie die Logik der Komponente perfekt beschreiben. Hier ist die AddPaste-Komponente und ihre Vorlage.

Die Tests sollten weiterhin fehlschlagen, da der Spion von addPaste eine solche Methode im PastebinService nicht findet. Kehren wir zum PastebinService zurück und legen etwas Fleisch darauf.

Schreiben der Tests für Dienste

Bevor wir mit dem Schreiben weiterer Tests fortfahren, fügen wir dem Pastebin-Dienst Code hinzu.

addPaste() ist die Methode des Dienstes zum Erstellen neuer Pasten. http.post gibt ein Observable zurück, das mit der toPromise()-Methode in ein Versprechen umgewandelt wird. Die Antwort wird in das JSON-Format umgewandelt, und alle Laufzeitausnahmen werden von handleError() abgefangen und gemeldet.

Sollten wir nicht Tests für Dienstleistungen schreiben, könnten Sie fragen? Und meine Antwort ist ein klares Ja. Services, die über Dependency Injection(DI) in Angular-Komponenten injiziert werden, sind ebenfalls fehleranfällig. Darüber hinaus sind Tests für Angular-Dienste relativ einfach. Die Methoden in PastebinService sollten den vier CRUD-Operationen ähneln, mit einer zusätzlichen Methode zur Behandlung von Fehlern. Die Methoden sind wie folgt:

  • handleError()
  • getPastebin()
  • addPaste()
  • updatePaste()
  • deletePaste()

Wir haben die ersten drei Methoden in der Liste implementiert. Versuchen wir, Tests für sie zu schreiben. Hier ist der Beschreibungsblock.

Wir haben TestBed.get(PastebinService) verwendet, um den realen Service in unsere Tests einzufügen.

getPastebin gibt ein Array von Pastebin-Objekten zurück. Die Typprüfung zur Kompilierungszeit von TypeScript kann nicht verwendet werden, um zu überprüfen, ob der zurückgegebene Wert tatsächlich ein Array von Pastebin-Objekten ist. Daher haben wir Object.getOwnPropertNames() verwendet, um sicherzustellen, dass beide Objekte dieselben Eigenschaftsnamen haben.

Der zweite Test folgt:

Beide Tests sollten bestehen. Hier sind die restlichen Tests.

Überarbeiten Sie pastebin.service.ts mit dem Code für die Methoden updatePaste() und deletePaste().

Zurück zu den Komponenten

Die verbleibenden Anforderungen für die AddPaste-Komponente lauten wie folgt:

  • Durch Drücken der Schaltfläche Speichern sollte die addPaste()-Methode des Pastebin-Dienstes aufgerufen werden.
  • Wenn der Vorgang addPaste erfolgreich ist, sollte die Komponente ein Ereignis ausgeben, um die übergeordnete Komponente zu benachrichtigen.
  • Durch Klicken auf die Schaltfläche Schließen sollte die ID 'source-modal' aus dem DOM entfernt und die showModal-Eigenschaft auf false aktualisiert werden.

Da sich die obigen Testfälle mit dem Modalfenster befassen, ist es möglicherweise eine gute Idee, verschachtelte Beschreibungsblöcke zu verwenden.

Das Deklarieren aller Variablen an der Wurzel des Beschreibungsblocks ist aus zwei Gründen eine gute Vorgehensweise. Auf die Variablen kann innerhalb des Beschreibungsblocks zugegriffen werden, in dem sie deklariert wurden, und der Test wird lesbarer.

Der obige Test verwendet die querySelector()-Methode, um inputTitle, SelectLanguage und textAreaPaste ihre jeweiligen HTML-Elemente (<input>, <select> und <textArea>) zuzuweisen. Als Nächstes werden die Werte dieser Elemente durch die Eigenschaftswerte von mockPaste ersetzt. Dies entspricht einem Benutzer, der das Formular über einen Browser ausfüllt.

element.dispatchEvent(new Event("input")) löst ein neues Eingabeereignis aus, um die Vorlage darüber zu informieren, dass sich die Werte des Eingabefelds geändert haben. Der Test erwartet, dass die Eingabewerte in die newPaste-Eigenschaft der Komponente übertragen werden.

Deklarieren Sie die newPaste-Eigenschaft wie folgt:

Und aktualisieren Sie die Vorlage mit dem folgenden Code:

Die zusätzlichen Divs und Klassen gelten für das modale Fenster des Bootstraps. [(ngModel)] ist eine Angular-Direktive, die die bidirektionale Datenbindung implementiert. (click) = "onClose()" und (click) = "onSave()" sind Beispiele für Ereignisbindungstechniken, mit denen das Klickereignis an eine Methode in der Komponente gebunden wird. Weitere Informationen zu verschiedenen Datenbindungstechniken finden Sie in der offiziellen Vorlage Syntax-Anleitung von Angular.

Wenn ein Template Parse-Fehler auftritt, liegt dies daran, dass Sie das FormsModule nicht in die AppComponent importiert haben.

Fügen wir unserem Test weitere Spezifikationen hinzu.

component.onSave() entspricht dem Aufruf von triggerEventHandler() für das Element Save button. Da wir die Benutzeroberfläche für die Schaltfläche bereits hinzugefügt haben, klingt der Aufruf von component.save() sinnvoller. Die Expect-Anweisung prüft, ob der Spion angerufen wurde. Hier ist die endgültige Version der AddPaste-Komponente.

Wenn der onSave-Vorgang erfolgreich ist, sollte die Komponente ein Ereignis ausgeben, das der übergeordneten Komponente (Pastebin-Komponente) signalisiert, ihre Ansicht zu aktualisieren. Zu diesem Zweck dient addPasteSuccess, eine Ereigniseigenschaft, die mit einem @Output-Dekorator dekoriert ist.

Das Testen einer Komponente, die ein Ausgabeereignis ausgibt, ist einfach.

Der Test abonniert die Eigenschaft addPasteSuccess genau wie die übergeordnete Komponente. Die Erwartung gegen Ende bestätigt dies. Unsere Arbeit an der AddPaste-Komponente ist abgeschlossen.

Kommentieren Sie diese Zeile in pastebin.component.html aus:

Und aktualisieren Sie pastebin.component.ts mit dem folgenden Code.

Wenn Sie auf einen Fehler stoßen, liegt dies daran, dass Sie die AddPaste-Komponente in der Spezifikationsdatei der Pastebin-Komponente nicht deklariert haben. Wäre es nicht großartig, wenn wir alles, was unsere Tests erfordern, an einem einzigen Ort deklarieren und in unsere Tests importieren könnten? Um dies zu erreichen, können wir entweder das AppModule in unsere Tests importieren oder stattdessen ein neues Modul für unsere Tests erstellen. Erstellen Sie eine neue Datei und nennen Sie sie app-testing-module.ts:

Jetzt können Sie ersetzen:

mit:

Die Metadaten, die providers und declarations definieren, sind verschwunden. Stattdessen wird das AppTestingModule importiert. Das ist ordentlich! TestBed.configureTestingModule() sieht schlanker aus als zuvor.

Anzeigen, Bearbeiten und Löschen Einfügen

Die ViewPaste-Komponente übernimmt die Logik zum Anzeigen, Bearbeiten und Löschen einer Einfügung. Das Design dieser Komponente ähnelt dem, was wir mit der AddPaste-Komponente gemacht haben.

Mock design of the ViewPasteComponent in edit modeMock design of the ViewPasteComponent in edit modeMock design of the ViewPasteComponent in edit mode
Bearbeitungsmodus
Mock design of the ViewPasteComponent in view modeMock design of the ViewPasteComponent in view modeMock design of the ViewPasteComponent in view mode
Ansichtsmodus

Die Ziele der ViewPaste-Komponente sind nachfolgend aufgeführt:

  • Die Vorlage der Komponente sollte eine Schaltfläche namens Ansicht Einfügen enthalten.
  • Wenn Sie auf die Schaltfläche Ansicht Einfügen klicken, sollte ein modales Fenster mit der ID "source-modal" angezeigt werden.
  • Die Einfügedaten sollten sich von der übergeordneten Komponente zur untergeordneten Komponente ausbreiten und im modalen Fenster angezeigt werden.
  • Durch Drücken der Bearbeitungstaste sollte component.editEnabled auf true gesetzt werden (editEnabled wird verwendet, um zwischen Bearbeitungsmodus und Ansichtsmodus umzuschalten).
  • Durch Klicken auf die Schaltfläche Speichern sollte die updatePaste()-Methode des Pastebin-Dienstes aufgerufen werden.
  • Ein Klick auf die Schaltfläche Löschen sollte die Methode deletePaste() des Pastebin-Dienstes aufrufen.
  • Erfolgreiche Aktualisierungs- und Löschvorgänge sollten ein Ereignis auslösen, um die übergeordnete Komponente über Änderungen an der untergeordneten Komponente zu informieren.

Lassen Sie uns anfangen! Die ersten beiden Spezifikationen sind identisch mit den Tests, die wir zuvor für die AddPaste-Komponente geschrieben haben.

Ähnlich wie zuvor werden wir einen neuen Beschreibungsblock erstellen und den Rest der Spezifikationen darin platzieren. Durch das Verschachteln von Beschreibungsblöcken auf diese Weise wird die Spezifikationsdatei besser lesbar und das Vorhandensein einer Beschreibungsfunktion aussagekräftiger.

Der verschachtelte Beschreibungsblock verfügt über eine beforeEach()-Funktion, mit der zwei Spione initialisiert werden, einer für die updatePaste()-Methode und der andere für die deletePaste()-Methode. Vergessen Sie nicht, ein mockPaste-Objekt zu erstellen, da unsere Tests darauf basieren.

Hier sind die Tests.

Der Test setzt voraus, dass die Komponente über eine paste-Eigenschaft verfügt, die Eingaben von der übergeordneten Komponente akzeptiert. Zuvor haben wir ein Beispiel dafür gesehen, wie von der untergeordneten Komponente ausgegebene Ereignisse getestet werden können, ohne dass die Logik der Hostkomponente in unsere Tests einbezogen werden muss. In ähnlicher Weise ist es zum Testen der Eingabeeigenschaften einfacher, die Eigenschaft auf ein Scheinobjekt festzulegen und zu erwarten, dass die Werte des Scheinobjekts im HTML-Code angezeigt werden.

Das modale Fenster enthält viele Schaltflächen, und es wäre keine schlechte Idee, eine Spezifikation zu schreiben, um sicherzustellen, dass die Schaltflächen in der Vorlage verfügbar sind.

Beheben wir die fehlgeschlagenen Tests, bevor wir komplexere Tests durchführen.

Es reicht nicht aus, die Paste anzeigen zu können. Die Komponente ist auch für das Bearbeiten, Aktualisieren und Löschen einer Paste verantwortlich. Die Komponente sollte über eine editEnabled-Eigenschaft verfügen, die auf true gesetzt wird, wenn der Benutzer auf die Schaltfläche Einfügen bearbeiten klickt.

Fügen Sie editEnabled=true; in die Methode onEdit() ein, um die erste expect-Anweisung zu löschen.

In der folgenden Vorlage wird mit der Anweisung ngIf zwischen dem Ansichtsmodus und dem Bearbeitungsmodus umgeschaltet. <ng-container> ist ein logischer Container, mit dem mehrere Elemente oder Knoten gruppiert werden.

Die Komponente sollte über zwei Output()-Ereignisemitter verfügen, einen für die updatePasteSuccess-Eigenschaft und einen für deletePasteSuccess. Der folgende Test bestätigt Folgendes:

  1. Die Vorlage der Komponente akzeptiert Eingaben.
  2. Die Vorlageneingaben sind an die paste-Eigenschaft der Komponente gebunden.
  3. Wenn der Aktualisierungsvorgang erfolgreich ist, gibt updatePasteSuccess ein Ereignis mit dem aktualisierten Einfügen aus.

Der offensichtliche Unterschied zwischen diesem Test und den vorherigen ist die Verwendung der fakeAsync-Funktion. fakeAsync ist mit async vergleichbar, da beide Funktionen zum Ausführen von Tests in einer asynchronen Testzone verwendet werden. Mit fakeAsync sieht Ihr Look-Test jedoch synchroner aus.

Die tick()-Methode ersetzt fixture.whenStable(). Then(), und der Code ist aus Entwicklersicht besser lesbar. Vergessen Sie nicht, fakeAsync zu importieren und von @angular/core/testing anzukreuzen.

Schließlich ist hier die Spezifikation zum Löschen einer Paste.

Wir sind fast fertig mit den Komponenten. Hier ist der endgültige Entwurf der ViewPaste-Komponente.

Die übergeordnete Komponente (pastebin.component.ts) muss mit Methoden aktualisiert werden, um die von der untergeordneten Komponente ausgegebenen Ereignisse zu verarbeiten.

Hier ist die aktualisierte pastebin.component.html:

Einrichten von Routen

Um eine geroutete Anwendung zu erstellen, benötigen wir einige weitere Lagerkomponenten, damit wir einfache Routen erstellen können, die zu diesen Komponenten führen. Ich habe eine Info-Komponente und eine Kontaktkomponente erstellt, damit wir sie in eine Navigationsleiste einfügen können. AppComponent enthält die Logik für die Routen. Wir werden die Tests für Routen schreiben, nachdem wir damit fertig sind.

Importieren Sie zunächst RouterModule und Routes in AppModule (und AppTestingModule).

Definieren Sie als Nächstes Ihre Routen und übergeben Sie die Routendefinition an die RouterModule.forRoot-Methode.

Alle am AppModule vorgenommenen Änderungen sollten auch am AppTestingModule vorgenommen werden. Wenn Sie jedoch beim Ausführen der Tests auf einen Fehler "No base href set" stoßen, fügen Sie dem providers-Array Ihres AppTestingModule die folgende Zeile hinzu.

Fügen Sie nun den folgenden Code zu app.component.html hinzu.

routerLink ist eine Direktive, mit der ein HTML-Element an eine Route gebunden wird. Wir haben es hier mit dem HTML-Ankertag verwendet. RouterOutlet ist eine weitere Anweisung, die die Stelle in der Vorlage markiert, an der die Ansicht des Routers angezeigt werden soll.

Das Testen von Routen ist etwas schwierig, da es mehr Interaktion mit der Benutzeroberfläche erfordert. Hier ist der Test, der prüft, ob die Ankerverbindungen funktionieren.

Wenn alles gut geht, sollten Sie so etwas sehen.

Screenshot of Karma test runner on Chrome displaying the final test resultsScreenshot of Karma test runner on Chrome displaying the final test resultsScreenshot of Karma test runner on Chrome displaying the final test results

Letzter Schliff

Fügen Sie Ihrem Projekt ein ansprechendes Bootstrap-Design hinzu und bedienen Sie Ihr Projekt, falls Sie dies noch nicht getan haben.

Zusammenfassung

Wir haben eine vollständige Anwendung von Grund auf in einer testgetriebenen Umgebung geschrieben. Ist das nicht toll? In diesem Tutorial haben wir gelernt:

  • entwerfen einer Komponente mithilfe des ersten Testansatzes
  • schreiben von Komponententests und grundlegenden UI-Tests für Komponenten
  • über Angulars Testdienstprogramme und wie man sie in unsere Tests einbezieht
  • Informationen zur Verwendung von async() und fakeAsync() zum Ausführen asynchroner Tests
  • die Grundlagen des Routings in Angular und des Schreibens von Routentests

Ich hoffe, Ihnen hat der TDD-Workflow gefallen. Bitte kontaktieren Sie uns über die Kommentare und teilen Sie uns Ihre Meinung mit!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.