Arbeitsplatz Bild eurofunk Kappacher GmbH

Cypress Component Tests

Description

Patrick Pichler von eurofunk Kappacher demonstriert in seinem devjobs.at TechTalk die Herangehensweise des Teams, wie sie Component Tests mit cypress durchführen.

Beim Videoaufruf stimmst Du der Datenübermittlung an YouTube und der Datenschutzerklärung zu.

Video Zusammenfassung

In "Cypress Component Tests" zeigt Patrick Pichler (eurofunk Kappacher GmbH), wie Cypress’ Component Testing die Lücke zwischen Unit- und End-to-End-Tests schließt—nachdem Angular-Unit-Tests (Konfiguration, DOM-Interaktion, Asynchronität, Change Detection, HTTP‑Mocks) mühsam waren und E2E/Storybook‑Setups langsam und aufwendig. Er demonstriert am Login-Beispiel den Runner und Techniken wie Mounten von Komponenten mit Providern, Custom Commands, Inputs (inkl. Signals) und Outputs (CreateOutputSpy), Template-basiertes Zusammenspiel, DI-Zugriff sowie Netzwerkmocking via cy.intercept. Ergebnis: eine vereinfachte CI (nur Cypress), in der rund 259 Tests in ca. drei Minuten laufen und lokal in Sekunden Feedback liefern—eine praxisnahe Blaupause, um Frontend‑Komponenten schneller und zuverlässiger zu testen.

Cypress Component Tests mit Angular: Wie Patrick Pichler (eurofunk Kappacher GmbH) die Lücke zwischen Unit und E2E-Tests schließt

Kontext: Hohe Qualitätsansprüche treffen auf reale Testschmerzen

In „Cypress Component Tests“ zeigt Patrick Pichler von eurofunk Kappacher GmbH, wie sein Team den Test-Stack im Frontend neu ausbalanciert. Pichler ist seit 17 Jahren in der Softwareentwicklung, fokussiert auf Frontend, und arbeitet an der EOX (Eurofunk Operations Center Suite). eurofunk Kappacher GmbH liefert komplette Leitstellenlösungen – von Möbeln über Server und Verkabelung bis hin zur Software, inklusive Multimedia-Integration und Videowänden. In der Produktentwicklung arbeitet Pichler im Bereich „Products Foundation“, der Plattformlösungen baut, damit Stream-aligned Teams Features schneller liefern können.

Vor diesem Hintergrund ist Testqualität kein Luxus. Sie entscheidet pragmatisch über Arbeitsgeschwindigkeit und Zuverlässigkeit. Die Testpyramide liefert zwar die bekannte Richtung – viele Unit-Tests, weniger Integrationstests, wenige End-to-End-Tests –, aber die gelebte Realität in Pichlers Bereich sah anders aus: viele Unit-Tests, fast keine Integrationstests und sehr viele End-to-End-Tests mit Cypress. Das war kein bewusstes Ziel, sondern „ist einfach passiert“ – getrieben vom Tech-Stack (Angular) und den Stolpersteinen im klassischen Komponententesten.

Wo klassische Angular-Tests weh tun

Pichler benennt fünf Schmerzfelder, die in Angular-Unittests immer wieder produktiv stören: Testkonfiguration, DOM-Interaktion, Asynchronität, Change Detection (Synchronisation) und HTTP-Mocks.

1) Testkonfiguration

  • Verstehen des Angular-Modulsystems macht das Setup schwergewichtiger. Neuere Angular-Versionen mit „standalone“ entschärfen das, aber die Hürde bleibt spürbar.
  • Alle Abhängigkeiten müssen testseitig korrekt bereitgestellt werden. Man muss wissen, wie die Komponente in der App verkabelt ist, und das im Testbett replizieren.
  • Strategiefrage: Was mocke ich? Alles? Nur Services? Angular-Core-Features wie Routing oder HTTP müssen oft explizit gemockt werden.

2) DOM-Interaktion

  • Banale Checks erzeugen überraschend viel Boilerplate. Ein einfacher Zugriff auf ein P-Tag und dessen Textinhalt ist nicht schwer – aber umständlich.
  • Inputs und reaktive Elemente verlangen die präzise Simulation nativer Browserverhalten. Wer hier die falschen Events triggert, testet am Nutzer vorbei. Das ist kein Angular-spezifisches Problem und trifft insbesondere Einsteigerinnen und Einsteiger.

3) Asynchronität

  • „Asynchrony is just hard in general.“ Die Unterscheidung und richtige Nutzung von fakeAsync und async ist nicht trivial.
  • tick und flush in fakeAsync sind erklärungsbedürftig; Fehlerbilder sind oft Timing-getrieben und damit schwer zu debuggen.
  • zone.js patcht Funktionen wie setTimeout und setInterval. Reines async/await kann an Angulars Mechanik vorbei arbeiten – mit überraschenden Effekten.

4) Change Detection (Synchronisation)

  • Im Test muss Change Detection teilweise manuell getriggert werden, anders als im echten App-Laufzeitverhalten.
  • In Kombination mit Asynchronität entstehen Timing-Fallen.
  • Die Strategie OnPush steigert die Komplexität noch einmal.

5) HTTP-Mocks

  • Der HttpTestingController fügt Komplexität hinzu, die man erst erlernen muss.
  • Wieder kommen Asynchronität, Change Detection und HTTP zusammen – ein Dreiklang, der in Summe fehleranfällig ist.
  • Fehlerfälle zu mocken ist weniger „straightforward“ als die API vermuten lässt.

Kurz: All das drückt die Ergonomie und verlagert Tests im Zweifel nach oben in E2E-Szenarien – genau das, was die Testpyramide vermeiden will.

E2E mit Cypress: stark – aber teuer im Durchlauf

eurofunk Kappacher GmbH setzt im End-to-End-Bereich auf Cypress. Der bekannte Runner zeigt Testschritte links und rendert rechts die echte Anwendung. Pichler spricht von „rund 1.000“ E2E-Tests (als Daumenzahl per Dateisuche ermittelt) – nicht nur in seinem Projekt, auch in anderen Teams.

Mit so viel Cypress-Know-how lag ein naheliegender Gedanke auf dem Tisch: Können wir Cypress gegen isolierte Komponenten laufen lassen und so Frontend-Integrationstests beschleunigen? Der erste Ansatz kombinierte Cypress mit Storybook.

Storybook + Cypress: theoretisch gut, praktisch zu langsam

Die Pipeline sah so aus:

  1. Storybook bauen (Artefakte erzeugen)
  2. Node-basierte HTTP-Serverinstanz starten, die die Storybook-Artefakte ausliefert
  3. Cypress bauen (Tests bündeln)
  4. Cypress-Tests gegen den lokalen Server ausführen

Das funktionierte – aber lief in der Praxis „10+ Minuten“ selbst für wenige Tests. Zwei Build-Prozesse, ein zusätzlicher HTTP-Server und Storybook (damals Version 6) bremsten die Feedbackschleife deutlich aus. Für schnelle Iteration ist das zu träge.

Allgemeine E2E-Schmerzpunkte

Pichler fasst die bekannten Nachteile von End-to-End-Tests zusammen:

  • Sie sind langsamer als Unit- oder Integrationstests. Nutzerinteraktionen und Reaktivität erzwingen Wartezeiten.
  • Längere Feedbackzyklen verlangsamen die Entwicklung. In seinem Umfeld laufen E2E-Tests nicht als Teil jeder CI-Ausführung, sondern mehrmals täglich zeitgesteuert – das Feedback kommt oft Stunden nach dem Deployment, inklusive Kontextwechselkosten.
  • Flakiness unterminiert Vertrauen. Netzwerkrandbedingungen und Umgebungsfaktoren lassen Tests sporadisch kippen.
  • Komplexes Setup und Debugging. Man debuggt eher eine Mini-Produktionslandschaft als eine isolierte Komponente.
  • Infrastrukturkosten und realistische Testdatenverwaltung sind echte Aufwände.

Genau hier positioniert Pichler Cypress Component Testing als passenden Mittelweg.

Cypress Component Testing: Isolation mit realer DOM-Interaktion

Cypress Component Testing sitzt zwischen Unit- und E2E-Tests. Unterstützte Frameworks sind React, Angular, Vue und Svelte (Stand zum Zeitpunkt des Vortrags). Der Ansatz: Komponenten isoliert mounten, über die echte DOM-Schicht mit ihnen interagieren und Cypress die Asynchronität abnehmen lassen.

Vorteile aus der Praxis

  • Komponenten laufen isoliert – mit der Präzision und Kontrolle von Unit-Tests.
  • Keine manuelle Asynchronitätsverwaltung: Cypress übernimmt das „Warten“, wie bei E2E.
  • Keine manuelle Change Detection: Die Komponente verhält sich „wie in der App“ und nicht wie in einer künstlichen Testhülle.
  • Starke Developer Experience: Der visuelle Runner zeigt Schritte, Zustände, Screenshots und bei Bedarf CI-Videos.
  • Tempo: Ohne Backend-Setup und ohne Storybook/Server ist die Schleife deutlich schneller.

„Der Developer Experience ist großartig … und natürlich ist die Geschwindigkeit erhöht.“

Grenzen und Trade-offs

  • Testkonfiguration bleibt notwendig: Wer eine Komponente mountet, muss ihre Abhängigkeiten kennen und bereitstellen.
  • Zeitkontrolle: Die Übernahme der Uhr durch Cypress steht derzeit im Konflikt mit Change-Detection-Algorithmen in Angular und React. Ein offenes Bug-Ticket existiert.
  • Tests ohne DOM-Interaktion machen wenig Sinn in einem UI-orientierten Framework.

Pipeline nach der Migration: ein Kommando, Minuten statt Zehn-Minuten-Blöcke

Pichlers Team strich die Storybook- und HTTP-Server-Schritte komplett. Die neue CI-Pipeline: Cypress als einziger Schritt, der buildet und testet.

  • Beispielprojekt mit 259 Tests: etwa 3 Minuten Laufzeit in CI (inklusive Kompilierung und Testausführung).
  • Lokal: Einmalige Kompilierung, dann Watcher; kompletter Lauf in etwa 1 Minute.
  • Während der Entwicklung: Fokus auf einzelne Tests mit Laufzeiten „in Sekunden“.
  • Zielvorgabe: Feedback unter 5 Minuten pro Pipeline-Job – wird erfüllt.

Für die Product-Foundation-Rolle bedeutet das: schnellere, häufigere Rückmeldungen und weniger Reibung im Tagesgeschäft.

Die Demo: Login-Formular, Komponenten-Mount und Debug-Erlebnis

Zur Veranschaulichung nutzte Pichler ein Login-Formular („GooseCorp“) mit Live-Validierung und einem zeitverzögerten Login-Ergebnis (Banner nach einigen Sekunden).

Cypress-Runner und Browserwahl

Beim Start trennt Cypress klar zwischen End-to-End und Component Tests. Entwicklerinnen und Entwickler können den Zielbrowser auswählen, etwa Chrome, und somit Verhalten und Styling in verschiedenen Browsern gegentesten.

Sichtbarkeit der Testschritte

Der Runner zeigt links die Testschritte und rechts die gerenderte Komponente. Besonders wertvoll: das Debugging.

  • Schritte können einzeln geöffnet werden; Cypress hebt die getroffenen DOM-Elemente im rechten Panel hervor.
  • Pinning eines Elements ermöglicht das genaue Inspizieren in den DevTools – inklusive Anzahl gefundener Elemente und Metadaten.
  • Bei Interaktionen zeigt Cypress koordinatengenau, wo geklickt wurde, und stellt sicher, dass das Element wirklich klickbar ist (sichtbar, nicht überlagert). Andernfalls bricht Cypress mit einer klaren Fehlermeldung ab.

„Cypress prüft, ob ein Element überhaupt klickbar ist … und wirft einen Fehler, wenn die Koordinaten kein Klick-Event erzeugen würden.“

Teststruktur und Mounting: vertraute Syntax, gezielte Provider

Cypress Component Tests nutzen gewohnte Test-Semantik: describe, beforeEach, it. Zentrales Element ist das Mounten der Komponente. Hier liegt der Rest an notwendiger Testkonfiguration.

  • Im Beispiel mountet Pichler die Login-Komponente und liefert die benötigten Provider mit – u. a. eine Animationskonfiguration sowie eine interne Icon-Bibliothek über provideIcons.
  • DOM-Zugriffe erfolgen via cy.get mit jQuery-ähnlicher Syntax. Assertions wie be.visible sind direkt verfügbar, inklusive Autovervollständigung.
  • type simuliert echte Nutzereingaben Zeichen für Zeichen und triggert alle relevanten Browser-Events. click interagiert robust und prüft die Klickbarkeit.

Dieses Setup reduziert die kognitive Last: statt Event-Simulationen und Change-Detection-Hilfen sind Mounting und Interaktion im Vordergrund.

Komfortschicht: Custom Commands für Mounting und Selektoren

Damit Testfälle noch kompakter werden, setzt Pichlers Team Custom Commands ein:

  • Ein eigenes mount-Kommando liefert immer die Default-Provider mit, etwa Animationen und die Icon-Bibliothek. Jede Komponente profitiert davon, ohne dass Testautorinnen und -autoren Boilerplate wiederholen.
  • Ein getByQA-Kommando kapselt den Zugriff über data-qa-Attribute, sodass Selektoren stabil und lesbar bleiben.

Das Ergebnis: weniger Rauschen im Testcode, klarer Fokus auf das Verhalten.

Inputs, Live-Updates und Outputs: Komponente wie in der App ansteuern

Inputs beim Mounten

Über „component properties“ im Mounting-Call lassen sich Eingaben direkt setzen, z. B. eine benutzerdefinierte Info-Nachricht, die statt der Standardmeldung angezeigt wird.

Live-Updates mit Signals

Um Eingaben nach dem Mounten zu verändern, nutzt Pichler Signals. Ein Signal wird erstellt, als Input gebunden und später auf einen neuen Wert gesetzt. Change Detection und UI-Update passieren automatisch – genau das gewünschte Verhalten für dynamische Testszenarien. Details zu Signals vertieft Pichler im Talk nicht; hier bleibt der Fokus auf der praktischen Verwendbarkeit im Testlauf.

Outputs mit CreateOutputSpy

Events der Komponente (Outputs) werden über CreateOutputSpy verifiziert. Beispiel: Ein Output afterLogin wird mit einem Spy hinterlegt, der später über ein Alias adressiert und mit bekannten Mustern geprüft wird – etwa „wurde das Event genau einmal ausgelöst?“ oder „kam der erwartete Parameter an?“. Die Syntax entspricht dem, was viele bereits aus Unit-Testing kennen.

Integration auf Komponentenebene: Template-Syntax für zusammenspielende Bausteine

Nicht jede Frage lässt sich an einer Single-Komponente beantworten. Für typische Kombinationen – Pichler nennt z. B. ein Akkordeon mit Panels – bietet Cypress Component Testing die Möglichkeit, über eine Template-String-Syntax mehrere Komponenten zusammenzusetzen. Inputs und Outputs werden wie in Angular üblich gebunden.

Wichtiges Detail: Die componentProperties beziehen sich dann auf eine generische Host-Komponente. Es ist sinnvoll, eine Interface-Definition zu pflegen, die alle gebundenen Inputs und Spies abbildet, etwa infoMessage und afterLoginSpy. So bleiben die Tests typsicher und nachvollziehbar.

Zugriff auf Instanzen und DI: Fixture, Injector und Component Instance

Nach dem Mounten stellt Cypress einen Wrapper bereit, der u. a. eine fixture enthält. Darüber lässt sich der Angular-Injektor abrufen, um Services zu beziehen, Spies zu setzen oder Interaktionen auszulösen. Zusätzlich ist die eigentliche Komponenteninstanz zugreifbar, wenn etwa statische Eigenschaften oder interne Zustände verifiziert werden sollen. Pichler zeigt den Injector-Weg exemplarisch; der direkte Zugriff auf die Komponenteneigenschaft ist ebenfalls Teil des Wrappers.

HTTP-Mocking: drei Wege – einer ist meist der richtige

Komponenten werden früher oder später Netzwerkaufrufe auslösen. Im Test will man das vermeiden – schon aus Stabilitäts- und Geschwindigkeitsgründen.

Pichler demonstriert drei Herangehensweisen:

  1. Service überschreiben: Den aufrufenden Service durch ein Mock ersetzen und Testdaten zurückgeben. Einfach und effektiv.
  2. Angulars HttpTestingController verwenden: Über provideHttpTestingClient einrichten, Requests abfangen und Antworten „flushen“. Möglich und lehrreich, aber in diesem Kontext nicht die bevorzugte Lösung.
  3. Cypress intercept nutzen: URL-Muster definieren, Antwort stubben, optional ein Alias vergeben und mit wait auf den Request warten. Danach Assertions gegen das UI vornehmen. In Pichlers Worten ist dies „wahrscheinlich der richtige Weg“ für diesen Anwendungsfall.

Der dritte Weg harmoniert am besten mit der Cypress-Philosophie: echte DOM-Interaktion, Cypress-gesteuertes Timing und klare, sichtbare Effekte in der Oberfläche.

Warum das alles wichtig ist: Geschwindigkeit, Vertrauen, Fokus

Die Kernbotschaft aus Pichlers Erfahrungsbericht:

  • Mit Cypress Component Testing lässt sich die Testpyramide in der Praxis wieder strukturieren. Isolierte, schnelle Tests fangen viele Fälle ab, die sonst in E2E landen würden.
  • Cypress übernimmt die raue Kante von Asynchronität und Interaktions-Details. Entwicklerinnen und Entwickler konzentrieren sich auf Verhalten, nicht auf Event- oder Change-Detection-Handwerk.
  • Die Pipeline wird schlanker und liefert in Minuten Feedback. Das passt zu realen Zielen im CI/CD („unter 5 Minuten pro Job“).
  • E2E bleibt wichtig – aber nicht als Default für alles, was die Komponente betrifft. Flakiness, Infrastrukturkosten und Debug-Aufwand sollten für die „richtigen“ Szenarien aufgehoben werden.

„Cypress Component Tests sind ein großartiges Werkzeug, um die Lücke zwischen End-to-End und Unit zu füllen.“

Konkrete Takeaways für Angular-Teams

  • Behalte die Testkonfiguration im Griff. Ein dediziertes mount-Custom-Command mit Default-Providern spart auf Dauer enorm Zeit und Nerven.
  • Nutze data-qa-Attribute und ein getByQA-Kommando, um robuste Selektoren zu etablieren.
  • Simuliere Nutzerverhalten über Cypress-Kommandos wie type und click. Vertraue auf die integrierten Checks (z. B. „klickbar“), statt eigene Heuristiken zu basteln.
  • Binde Inputs beim Mounten und nutze Signals für Live-Updates. So testest du dynamische UIs realitätsnah, ohne manuelle Change Detection.
  • Verifiziere Outputs mit CreateOutputSpy. Das fühlt sich an wie Unit-Testing – nur mit echter DOM-Oberfläche.
  • Für zusammenspielende Komponenten setze auf die Template-Syntax und kapsle die Bindungen in ein Host-Interface.
  • Wenn HTTP ins Spiel kommt, beginne mit intercept. Überschreibe Services, wenn es die Architektur nahelegt; greife zum HttpTestingController nur, wenn du einen triftigen Grund hast.
  • Plane E2E-Tests dort, wo Systemintegration, Netz und reale Infrastruktur zählen. Für Komponentenlogik und Interaktion ist Component Testing die effizientere erste Wahl.

Fazit

Patrick Pichler zeigt, wie Cypress Component Testing in einem Angular-Setup praktische Bremsklötze entfernt: weniger Boilerplate, weniger manuelle Asynchronität, weniger Change-Detection-Tricks – dafür mehr echte Interaktion, Sichtbarkeit und Geschwindigkeit. Die Migration weg von Storybook-getriebenen Tests hin zu reinem Cypress Component Testing verschlankt die Pipeline auf „ein Kommando“ und drückt die Laufzeiten von „10+ Minuten“ auf wenige Minuten für hunderte Tests.

Für Teams, die – wie bei eurofunk Kappacher GmbH – hochwertige Frontends unter realen Randbedingungen liefern müssen, ist das ein überzeugender Hebel. Die Testpyramide wird wieder zur Leitlinie: Unit- und Komponententests fangen den Großteil ab, E2E deckt die Integrationsspitzen ab. Ergebnis: schnellere Feedbackschleifen, höhere Teststabilität und mehr Vertrauen in das, was wirklich zählt – die Qualität des ausgelieferten Verhaltens.

Weitere Tech Talks

Weitere Tech Lead Stories

Weitere Dev Stories