7. Mai 2019
Unit-Tests in Vue.js: Von der Theorie zur Praxis
Ein Ansatz für Unit-Tests in Vue.js-Projekten.

Zeit zum Testen
Die Qualitätssicherung für Softwareprodukte und -dienstleistungen erstreckt sich in der Regel über mehrere Teams, von den Projektverantwortlichen über die Entwickler bis hin zu speziellen QA-Teams. Durch den standardisierten Einsatz von CI/CD-Pipelines zur Bereitstellung von Software sind automatisierte Tests mittlerweile fester Bestandteil des täglichen DevOps-Alltags geworden.
Automatisierte Softwaretests lassen sich im Allgemeinen in drei Gruppen einteilen, die in der Testpyramide visuell dargestellt werden:
- Unit-Tests
- Integrationstests
- End-to-End-Tests
Die Ebenen der Pyramide veranschaulichen, welchen Anteil die einzelnen Testarten innerhalb eines Softwareprojekts abdecken sollten.
Testgetriebene Entwicklung (TDD) ist ein Softwareentwicklungsprozess, bei dem der Schwerpunkt von Beginn an – sobald ein Entwickler die erste Codezeile schreibt – auf Qualitätssicherung und Unit-Tests liegt. In ihrer „reinsten“ Form beginnt TDD immer mit dem Schreiben eines Unit-Tests und endet, wenn die anhand des Tests geschriebene Software den Test besteht.
Was ist eigentlich Unit-Testing?
Unit-Tests lassen sich ganz einfach definieren: Es handelt sich um das Testen einzelner Softwareeinheiten isoliert voneinander.
Genauer gesagt lässt sich eine Softwareeinheit wie folgt definieren: der kleinste, testbare Teil einer Software. In der Welt der funktionalen Programmierung (FP) sind reine Funktionen die unbestrittenen Einheiten, für die Tests geschrieben werden. Betrachten wir zum Beispiel den entsprechenden Begriff:
Ein Vuex-Store-Modul kann ebenfalls aus verschiedenen Mutations- und/oder Getter-Funktionen bestehen:
Etwas interessanter wird es, sobald Komponenten ins Spiel kommen:
Aber wie sieht es mit komplexeren (und typischen) Vue.js-Komponenten aus, die Vuex-Funktionalität abbilden:
In diesem Szenario spielen die letzten beiden Wörter der Definition von „Unit Testing“ eine entscheidende Rolle: „isoliert“. Ein Test des fetchList Klick-Handlers für diese Komponente wäre nicht erforderlich, da die eigentliche Implementierung bereits bei der Erstellung des Vuex-Stores getestet worden wäre.
Die allgemeine Regel „Teste keine Abhängigkeiten und keine Abhängigen“ lässt sich bei Unit-Tests von Vue.js-Komponenten mittlerer bis hoher Komplexität anwenden:
- Vermeide es, Vuex-Store-Funktionen direkt zu testen.
- Vermeide es, UI-/CSS-Bibliotheken zu testen, z. B. Vuetify, material-web-components.
- Vermeide es, API-Module zu testen.
- Vermeide es, … alles zu testen, was Nebenwirkungen erzeugt
Bewährtes
Als Entwickler und als Teil eines größeren Produktentwicklungsteams bietet die Einbindung von Unit-Tests in den Entwicklungsprozess vielfältige Vorteile. Aus der Perspektive eines Entwicklers führt ein TDD-Ansatz mit Schwerpunkt auf Unit-Tests in der Regel zu Code, der modularer (komplexe Architekturen, die sich aus Einheiten zusammensetzen), vorhersehbarer und letztlich besser wartbar ist. Auch aus Sicht des Produktlebenszyklus ermöglicht die Erstellung von Unit-Test-Skripten zu Beginn des Sprints dem Entwickler, unklare Akzeptanzkriterien vor der Implementierung von Funktionen mit dem Product Owner zu klären.
Ein typischer TDD-Zyklus könnte etwa wie folgt aussehen:
- Einen fehlschlagenden Test anhand der Akzeptanzkriterien schreiben (bei Bedarf klären)
- Die Funktion im Code implementieren
- Den Code anhand des Tests ausführen
- Die Schritte 2–3 wiederholen, bis alle Testszenarien für die Unit bestanden sind
Mit vue-cli 3 ist die Einrichtung eines neuen oder bestehenden Vue.js-Projekts für Unit-Tests ein Kinderspiel – es genügt ein einziger Befehl im Terminal (oder grafisch über die Vue-CLI-Benutzeroberfläche):
Ein 5-Punkte-Konzept
Der Umstieg auf einen TDD-basierten Prozess mag zunächst einschüchternd wirken, doch es könnte hilfreich sein, einen Ansatz zu entwickeln, der folgende allgemeine Punkte berücksichtigt:
1. White-Box-Tests durchführen
Beim White-Box-Testing werden Softwareeinheiten in einer Testumgebung isoliert und die eingegebenen Daten anhand der erwarteten Ausgabe überprüft. Im Gegensatz zum Black-Box-Testing, bei dem die Implementierungsdetails der getesteten Software undurchsichtig sind, erfordert das White-Box-Testing transparente Kenntnisse über die Implementierung der Einheiten. Dies wird deutlich, wenn komplexere Teile einer Vue.js-Anwendung getestet werden, beispielsweise eine Aktionsfunktion in einem Vuex-Store. Die Präsentation beschreibt anschaulich den Unterschied zwischen White-Box-, Black-Box- (E2E) und Grey-Box- (Integrations-)Tests.
2. Szenarien mithilfe einfacher Assertions validieren
Das Sprichwort „Simple is better“ gilt auch für den Ansatz, Eingabewerte anhand der Ausgabewerte zu überprüfen. Wenn von einer Funktion erwartet wird, dass sie ein Array mit drei Zeichenketten zurückgibt, sollte die folgende Überprüfung vermieden werden:
Diese Aussage ist klar und verständlich:
Anhand des vorherigen Beispiels zur Factorial-Funktion überprüft die folgende Assertion mit Jest, ob die Funktion die 5. und 10. Factorial-Ausdrücke korrekt berechnet:
Die Präsentation und das dazugehörige Repository enthalten weitere Beispiele für die Erstellung von Assertions für Vuex-Mutationen/Getter und -Aktionen (mit Jest-Mocks).
3. Stellen Sie sicher, dass die Tests in einer isolierten Umgebung (d. h. Testsoftware/-system) durchgeführt werden.
4. Wählen Sie die geeigneten (d. h. bewährten) Werkzeuge aus
- framework/runner/assertion library
Viele Frameworks bieten offiziell unterstützte Unit-Test-Frameworks, bei denen sich Entwickler kaum um die Konfiguration kümmern müssen. Mit vue-test-utils stehen sofort einsatzbereite Unit-Tests zur Verfügung, wobei Jest nicht nur als Test-Framework, sondern auch als Runner und Assertion-Bibliothek dient.
Mit vue-test-utils werden einzelne Vue-Komponenten über eine Wrapper-basierte API mit der Methode shallowMount isoliert in eine JSDOM-Umgebung eingebunden. Im Gegensatz zu mount simuliert shallowMount nur die untergeordneten Komponenten einer Komponente und nicht den gesamten Unterbaum.
Beim Unit-Testing von Vue-Komponenten kann die Entscheidung, welche Codeabschnitte durch Tests abgedeckt werden sollen, kompliziert sein:
Wir empfehlen, Tests zu schreiben, die die öffentliche Schnittstelle Ihrer Komponente überprüfen … Die Schnittstelle einer Komponente umfasst mehrere Eingaben und die dazugehörigen Ausgaben.
- Inputs: props, user interaction, Vuex store
- Outputs: events, rendered/updated UI, mutations, actions
Ein subjektiver, aber praktischer Ansatz zur Minimierung von Doppelarbeit beim Testen wäre, das Testen aller Vuex-bezogenen Funktionen (z. B. mapActions oder mapMutations) innerhalb einer .vue SFC-Komponente auszuschließen, da die Testabdeckung gemäß dem Vertrag der Komponente bereits durch den Store gewährleistet sein sollte.
Snapshot-Tests können ebenfalls eine schnelle Möglichkeit sein, mit sehr wenigen Zeilen Testcode eine hohe Testabdeckung zu erzielen. Allerdings müssen die folgenden Einschränkungen berücksichtigt werden:
- Erzeugung großer Snapshot-Dateien
- False positives/negatives
- Deterministische Tests
5. Verwenden Sie Mocking nur in geringem Umfang
Zwar bietet Jest eine hervorragende Lösung für das Mocking von Funktionen, Modulen und Abhängigkeiten, doch ist die Verwendung zahlreicher Mocks in einem einzelnen Unit-Test ein deutlicher Hinweis auf eine enge Kopplung und übermäßige Komplexität des Codes – ein Anzeichen, das oft als „Code Smell“ bezeichnet wird. In solchen Fällen ließe sich die Software wahrscheinlich in kleinere Einheiten aufteilen.
Abschließendes Fazit
Diese Präsentation und dieser Artikel sollen dazu anregen, zu erkennen, dass Unit-Tests (mit Vue.js und generell) ein Prozess sind, der sich leicht in ein Entwicklungsteam integrieren lässt. Beginnen Sie JETZT mit (Unit-)Tests! In Verbindung mit soliden Prinzipien des Software-Designs (wie SOLID) geht es beim Programmieren und Testen von Software nicht mehr darum, wie viele Zeilen abgedeckt werden müssen, sondern darum, welche Teile der Codebasis abgedeckt werden sollten.
Über Unit-Tests hinaus wirft das Thema Integrationstests noch viele offene Fragen auf; erfahrungsgemäß sind die meisten in der Praxis auftretenden Fehler das Ergebnis von integrationsbezogenen Problemen (z. B. geänderte API-Annahmen/-Anforderungen). Die Einrichtung guter Integrationstests ist jedoch um ein Vielfaches komplexer als Unit-Tests. Der letzte Abschnitt der Präsentation behandelt einige grundlegende Aspekte von Integrationstests in Vue.js-Anwendungen (Vuex + .vue-Integration und Parent-Child-Integration).
Referenzen
- Software Testing Fundamentals
- Testing Vue.js Applications
- Vue.js Testing Workshop by Edd Yerburgh
- JavaScript Scene
- Companion repo
- Komplexere Szenarien



