Grüne Test-Pyramiden mit Cypress – UI-Testing für die Zukunft

Keine Kommentare

Cypress ist ein junges Open-Source-Test-Framework für Web-basierte, grafische Benutzeroberflächen. Cypress-Tests werden in JavaScript geschrieben und orientieren sich, wie auch bei Selenium-basierten Technologien üblich, am Document Object Model (DOM) des HTML einer Web-Anwendung.

Im Vergleich zum bekannten Vorfahren Selenium bietet Cypress viele Verbesserungen, die zu erhöhter Stabilität, Zuverlässigkeit sowie Laufzeit- und Entwicklungs-Geschwindigkeit führen. Die Vorteile des Youngstar und warum sich der Einsatz von Cypress lohnt, wollen wir uns im Verlauf dieses Artikels einmal näher anschauen.

Spitze Pyramiden: UI- statt Ende-zu-Ende-Tests

Zypressen als Symbolbild für die mit Cypress als Testing-Framework gelebte Test-Pyramide

Bevor man motiviert die ersten Cypress-Tests schreibt, sollte entschieden werden, was überhaupt getestet wird. Vor dem Start sollte man sich überlegen, ob lediglich die Oberfläche oder das ganze System (Ende-zu-Ende bzw. E2E) getestet werden soll. In der klassischen Pyramide koexistieren beide Arten von Tests. Dabei ist es gängig, wenige jedoch geschäftskritische E2E-Tests und viele detailtiefe Oberflächentests, losgelöst vom Backend, zu nutzen. Auch der Namensgeber des Frameworks, die Zypresse, bestätigt diesen Ansatz durch ihr Erscheinungsbild: spitz zulaufende Test-Pyramiden stehen für viele Unit-, UI- & Integrationstests sowie wenige E2E-Tests.

Warum sollten nicht alle Testfälle als E2E-Test gegen das ganze System gerichtet werden? Das erfahren Entwicklungsteams oft schmerzhaft. E2E-Tests sind langsamer, da von der zu testenden Oberfläche Anfragen an „echte“ Backends gestellt werden. Auch sind sie instabiler, da Tests von der Datenhaltung der Backends abhängig sind. In echten Umgebungen sind Daten dynamisch – eine schwierige Voraussetzung für automatisierte Tests. Daten ändern sich durch Synchronisationen aus Fremdsystemen, manuelle Interaktionen von Nutzern und werden zusätzlich noch mit einer abgeschwächten Konsistenz (Eventual Consistency) den Konsumenten zur Verfügung gestellt. Um darauf wiederum im Test zu reagieren, werden oft Warte- und Wiederholungs-Mechanismen eingebaut, die die Komplexität und den Frust im Team erhöhen. Das alles hat zur Folge, dass E2E-Tests selten deterministische Ergebnisse liefern, sondern häufiger fehlschlagen und damit den Entwicklungsprozess behindern können. E2E-Tests haben ihre Berechtigung, sind jedoch schlichtweg teurer.

Reine Oberflächentests, in denen die Backend-Anfragen durch kontrollierbare Daten ersetzt werden, eignen sich besser, um jede Funktionalität im Detail abzutesten. Sie können testgetrieben mit dem UI entwickelt werden und sind eine günstige Investition in eine gut getestete Codebasis.

Netzwerktraffic mit Cypress unter Kontrolle bringen

In reinen Oberflächentests profitiert man von der Kontrolle über Daten, die die Serverseite liefert. Cypress glänzt hier auf voller Linie, da dessen im nächsten Bild sichtbare Architektur dies vorteilhaft berücksichtigt.

Cypress besteht aus einem Node.JS Prozess, der auch als HTTP Proxy und Interceptor dient, sowie iFrames die im Browser die Anwendung und ihre Tests ausführen

Die Architektur während eines Tests besteht aus einem Node.js-Prozess (die „Serverseite“) und einem laufenden Browser. Im von Cypress instrumentalisierten Browser wird eine Seite aufgerufen, die die getestete Applikation wie auch ein Cypress-UI als iFrame einbettet. Der Testcode wird ebenfalls im Cypress-iFrame ausgeführt, sodass Testcode und Anwendungscode im gleichen Browser-Tab und somit auch im gleichen JavaScript-Loop laufen. Der Testcode kommuniziert über WebSockets mit dem Node-Prozess. Der Node-Prozess agiert als Proxy für jede HTTP-Anfrage aus dem Browser und kann sogar Shell-Kommandos ausführen.

Der Schlüssel zur Kontrolle des Netzwerktraffic liegt im Proxy. HTTP-Anfragen können aufgezeichnet werden. Somit kann im Test auf spezifische Anfragen gewartet werden, damit der Test erst fortfährt, wenn deren Antwort empfangen wurde. HTTP-Antworten können mit Fixtures überschrieben werden, anstatt die Anfragen an das echte Ziel zu senden und deren Antwort abzuwarten. Somit können wir die Antwort einer Anfrage fälschen, ohne die Anwendung gegen einen anderen Host konfigurieren zu müssen – das übernimmt alles der Proxy, wenn wir ihn über die richtigen Cypress-Befehle im Test dazu bringen.

Beispiel-Code, der demonstriert wie Cypress' fixture server genutzt werden kann um HTTP Responses zu überschreiben und aktiv auf diese zu warten

Außer HTTP-Anfragen können auch die lokale Zeit und sogar JavaScript-Objekte während der Laufzeit überschrieben (Stubs) oder observiert (Spies) werden. Das hilft, wenn von der Uhrzeit abhängige Teile der Anwendung deterministisch getestet werden sollen. Durch das Überschreiben von JavaScript-Objekten können wir der Anwendung sogar “vorspielen”, dass ein Benutzer eingeloggt ist, um uns auf die zu testende Kernfunktion konzentrieren zu können.

Ohne Selenium WebDriver für das moderne Web

Cypress trumpft, unabhängig von der Art des Testens, mit hoher Stabilität und Zuverlässigkeit im verglichen mit Selenium-basierten Tests. Selenium-Nutzer kennen die oft willkürliche und frustrierende „Flakiness“ von DOM-Assertions, welche zu fehlschlagenden Tests führt. Cypress baut nicht auf Selenium WebDriver oder ähnlichem auf, sondern wurde grundlegend neu für die Prinzipien heutiger Web-Anwendungen konzipiert und gebaut. Stark JavaScript-lastige Seiten und Single-Page-Applications (SPA) laden über HTTP-Anfragen und WebSockets kontinuierlich Ressourcen nach, führen DOM-Änderungen durch und sind stets in Bewegung.

Diese Dynamik lässt sich mit üblichen, statischen „waits“ und „sleeps“ schwer testen. Stattdessen wartet Cypress intelligent auf Kommandos, das Vorhandensein und sogar Animationen von DOM-Elementen und lässt „waits“ auf erwarteten HTTP-Requests definieren. Testing Code wird sauberer, lesbarer und weniger komplex. Die Ausführungsdauer reduziert sich auf die Zeit, die benötigt wird, um die Anwendung durch alle Status zu führen. Sie wird nicht mehr länger durch „sleeps“ und „waits“ verlangsamt.

Die Veränderungen der Oberfläche werden von Cypress zudem als DOM Snapshots aufgezeichnet. Somit kann auch nach der Ausführung des Tests im grafischen, interaktiven Modus abgespult werden, wie sich die Anwendung über den Test hinweg verändert hat. Zusätzlich können Videos der ganzen Ausführung sowie Screenshots bei Fehlschlägen aufgezeichnet werden, um bspw. Fehler während der Ausführung in einer Continuous Integration Pipeline untersuchen zu können. Sogar visuelle Regressionstests mit Cypress sind möglich. Hierbei wird das angestrebte Erscheinungsbild der Anwendung als Spezifikation gespeichert und das Ergebnis nach der Testausführung mit der Erwartung verglichen.

Flora und Fauna – Erweiterbarkeit von Cypress

Zypressen prägen das Landschaftsbild der schönen Toskana, kommen aber keinesfalls alleine vor. So entwickelt sich auch das florierende Ökosystem rund um Cypress in alle Richtungen weiter. Das Framework ist durch Custom Commands, Plugins und Events durch die Entwickler*innen erweiterbar.

Für viele Aufgabenstellungen, wie Login in E2E-Tests, Component Testing, File Uploads, Drag-and-Drop und viele mehr existieren bereits gute Cypress-Plugins. Ein gutes Beispiel für die Erweiterbarkeit von Cypress liefert auch der Artikel „BDD und End-to-End-Tests – Cypress mit Cucumber verbinden“ meines Kollegen Holger Grosse-Plankermann. Hiermit wird das Schreiben von Cypress-Testfällen in Gherkin-Syntax ermöglicht.

Cypress Custom Commands bieten sich auch an, um oft verwendeten Code (wie das Präparieren der Antwort eines Identity Providers) zu mocken, oder um fachliche Aktionen wie das Navigieren auf eine bestimmten Seite zu extrahieren.

Code-Beispiel, das die Verwendung von Cypress commands an einer gemockten Cognito Login-Anfrage demonstriert

Zum Ökosystem gehört auch der Cypress.io Dashboard-Service, mit Hilfe dessen Testergebnisse online aufgezeichnet, inspiziert und im Team sichtbar gemacht werden können. Er zählt zwar zum kostenpflichtigen Teil von Cypress.io, bietet jedoch auch ein Free Tier für kleine Projekte. Leider ist die Nutzung des Dashboard-Services für die Parallelisierung von Tests nötig, da ein serverseitiger Speicher benötigt wird. Dieser sammelt Metadaten über die Testausführungen, um eine sinnvolle Verteilung der Testfälle auf mehrere Worker zu gewährleisten. Um einen alternativen, unabhängigen Dashboard Service zur Verfügung zu stellen, kümmert sich ein Projekt namens „Sorry Cypress“.

Fazit

Cypress löst zweifelsfrei viele Probleme bisheriger Web-basierter Oberflächentests und hat für viele Entwickler*innen den Zahn der Zeit getroffen. In vielen Teams, die mit digitaler Produktentwicklung arbeiten, wird Oberflächen- und E2E-Tests noch nicht genügend Priorität eingeräumt. Das automatisierte Testing von UIs wird zwar theoretisch als vielversprechend angesehen, jedoch oft als komplex, zeitintensiv und unrobust wahrgenommen. Cypress befindet sich auf dem Weg, dieses Ansehen mit steigender Bekanntheit zu wenden und bringt positive Auswirkungen, wo es schon genutzt wird.

Für Teams, in denen eine Vielzahl an (teilweise alten) Browsern unterstützt werden muss, ist der eingeschränkte Browser-Support noch ein Manko. Bisher werden alle Chromium-basierten Browser (inklusive Microsoft Edge und Electron) sowie Mozilla Firefox (Beta-Support) unterstützt (Oktober 2020). Internet Explorer und Microsoft Edge 10 (nicht auf Chromium basierend) bleiben bisher auf der Strecke.

Auch die nur mit dem Dashboard-Service nutzbare Test-Parallelisierung wird häufig bemängelt. Jedoch schafft „Sorry Cypress“ zunehmend bessere Abhilfe, wenn man sich mit dem Betrieb eines eigenen Services arrangieren kann.

Zusammenfassend kann Cypress für Oberfläche- und Ende-zu-Ende-Tests uneingeschränkt empfohlen werden, wenn man auf automatisierte Tests mit veralteten Browsern verzichten kann.

Jonas Verhoelen

Jonas entwickelt leidenschaftlich gerne Software in agilen, crossfunktionalen Teams. Am liebsten begleitet er Web-Anwendungen ganzheitlich von der Idee bis zur Inbetriebnahme und darüber hinaus. Ursprünglich von der Java-Welt geprägt, entwickelt er aktuell bevorzugt mit React, Node.js und TypeScript. Zudem teilt er sein Fachwissen rund um Distributed Ledger Technologies und IT Security mit Kunden und der Community. An seinem Arbeitsumfeld schätzt er Selbstorganisation, Vertrauen, Transparenz und den Stellenwert von T-shaped skills.

Über 1.000 Abonnenten sind up to date!

Die neuesten Tipps, Tricks, Tools und Technologien. Jede Woche direkt in deine Inbox.

Kostenfrei anmelden und immer auf dem neuesten Stand bleiben!
(Keine Sorge, du kannst dich jederzeit abmelden.)

* Hiermit willige ich in die Erhebung und Verarbeitung der vorstehenden Daten für das Empfangen des monatlichen Newsletters der codecentric AG per E-Mail ein. Ihre Einwilligung können Sie per E-Mail an datenschutz@codecentric.de, in der Informations-E-Mail selbst per Link oder an die im Impressum genannten Kontaktdaten jederzeit widerrufen. Von der Datenschutzerklärung der codecentric AG habe ich Kenntnis genommen und bestätige dies mit Absendung des Formulars.

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.