Wie End-to-End-Testing endlich Spaß macht

In diesem Blogbeitrag zeige ich euch:
- warum ich mich endlich in das End-to-End-Testing von Webanwendungen verliebt habe
- wie Cypress genau das geschafft hat und wie man E2E-Tests mit Cypress aufsetzt
- einen Link zu meinem Beispielcode auf GitHub, der Cypress und TypeScript kombiniert
- weitere Informationen zu Continuous Integration
Hintergrund
Ich bin nun seit fast 20 Jahren professioneller Softwareentwickler und habe zahlreiche erfolgreiche Projekte aller Größenordnungen und in allen möglichen Branchen durchgeführt – größtenteils große Webanwendungen.
Test-Driven Development (TDD) ist etwas, von dem ich wirklich überzeugt bin (mehr dazu in späteren Blogbeiträgen), aber eines war immer schmerzhaft: End-to-End-Testing.
Wer von Selenium kommt, hat wahrscheinlich eine ähnliche Einstellung wie ich sie hatte:
Alles bei E2E-Tests ist langsam und umständlich. Alles:
- die lokale Einrichtung
- die Einrichtung auf dem CI-Server einschließlich Browser-Support
- das Entwickeln von Tests
- deren Ausführung
- deren Wartung
- das Nachverfolgen von False Positives
Um ehrlich zu sein: Selenium wurde erstmals 2004 veröffentlicht und hat über die Jahre viele Verbesserungen erfahren, aber das Web hat sich ebenfalls verändert. Dynamische Webanwendungen, die auf Angular, React oder Vue aufgebaut sind, auf der einen Seite – Dutzende neuer Browser und Endgeräte auf der anderen Seite.
Ich komme aus der Java-Ecke, weshalb ich Selenium immer im Blick behalten habe, auch weil mir die Programmiersprache vertraut war. Ich bin kein großer Fan davon, E2E-Tests aufzuzeichnen – ich möchte sie lieber selbst coden, wiederverwendbare Komponenten bauen und diese Tests im Wesentlichen wie Anwendungscode entwickeln. Aufgezeichnete Tests sind schwer zu warten, und obwohl man damit schnell Ergebnisse erzielt, landet man schnell in der Wartungshölle.
Bei Cloudflight entwickeln wir derzeit eine recht umfangreiche mobile App, und letztes Jahr haben wir auch eine Test-Suite auf Basis von Selenium und Browserstack implementiert. Wir haben viel Energie darauf verwendet, alles zum Laufen zu bringen, hatten Kontakt mit dem wirklich guten Support-Team von Browserstack, da unser APK dort regelmäßig abstürzte, hatten schließlich ein funktionierendes Test-Setup, auch in unsere CI-Pipeline integriert, einige Tests entwickelt – und dann passierte das, was mit fast jedem E2E-Testing-Projekt passiert, das ich in den letzten Jahren gesehen habe: Die Anwendung entwickelt sich weiter, die Tests nicht, Entwickler haben keine Lust mehr, die Tests zu warten (oder haben schlicht keine Zeit dafür), False Positives frustrieren alle, und plötzlich sammelt sich Staub auf diesen Tests an, und niemand kümmert sich mehr darum.
Einige Monate später sprang ich wieder in das Projekt, um kurz vor einem großen Release zu helfen. Da meine Frontend-Fähigkeiten sehr begrenzt sind, fragte ich das Team, ob es sinnvoll wäre, die Browserstack/Selenium-Tests wieder zum Laufen zu bringen. Ich klonte also das Git-Repo, folgte dem Readme, lud einige hundert MB Software herunter, installierte Webdriver, Android SDK und Appium für die lokale Entwicklung. Ich bat das Team auch um etwas Hilfe, und alle verdrehten die Augen und sagten Dinge wie „ja, das ist alles nicht so einfach”. Nach einigen Stunden hatte ich alles irgendwie zum Laufen gebracht, versuchte, die fehlschlagenden Tests zu reparieren, und es war einfach frustrierend. Die Feedback-Schleife war so lang, und herauszufinden, was genau das Problem bei den Tests war – wenn man von JUnit kommt – war so unglaublich umständlich, dass ich nach einigen Stunden aufgab.
Um ehrlich zu sein: Ich möchte weder Selenium noch Browserstack dafür verantwortlich machen. Diese Frameworks sind ausgereift und werden von zahlreichen Projekten weltweit eingesetzt, die Engineering-Teams hinter diesen Produkten sind kompetent und motiviert und haben großartige Arbeit geleistet (Selenium ist sogar kostenlos nutzbar). Wahrscheinlich haben wir es auch falsch aufgesetzt.
Also gab ich Cypress eine Chance. Ich, ein Java-Entwickler seit 20 Jahren, der nun JavaScript/TypeScript-E2E-Testing macht. Und was soll ich sagen? Nach 2 Stunden von Null hatte ich eine vollständige Test-Suite für den oben erwähnten Fotobuch-Designer. Technisch war das recht einfach möglich, da unsere App für Cross-Platform entwickelt wurde – mit einem Angular-Kern, Cordova für Mobile und Electron.js für Desktop –, aber es gibt auch eine reine Webversion auf derselben Quellbasis, sodass ich fast alle Anwendungsfälle im Browser testen kann, darunter:
- Login
- Produkte auswählen
- Bilder hochladen
- Ein Fotobuch gestalten
- Einige Einstellungen bearbeiten, wie den Titel des Buches
- Die Bestellung in den Warenkorb legen
- Den Checkout durchführen
- Lasst mich kurz die Grundlagen zu Cypress mit euch teilen:
Ein Cypress-Testprojekt erstellen
Die offizielle Cypress-Homepage hat bereits großartige Tutorials (beginnend mit npm install cypress und sehr kompakten Getting-Started-Tutorials), die ich hier nicht kopieren werde. Stattdessen werde ich Teile unseres Setups bei Cloudflight verwenden, das sich in der Teamarbeit bewährt hat.
Alles, was ihr tun müsst, ist eine package.json mit folgendem Inhalt zu erstellen (den Namen anpassen):
{
"name": "cypress-hashnode-sample",
"scripts": {
"cy:open": "cypress open"
},
"devDependencies": {
"cypress": "9.2.0"
}
}
Dann npm install und npm cy:open ausführen. Cypress wird beim ersten Start heruntergeladen (was eine Weile dauern kann), danach öffnet sich eine Anwendung mit folgendem Bildschirm:

Hier gibt es viel zu sagen, und wir werden später darauf zurückkommen. Zunächst bemerken wir zwei Dinge:
- Cypress hat automatisch Beispiel-Testdateien für uns generiert
- Wir können all diese Tests sofort ausführen, indem wir auf den Link „Run 20 integration specs” auf der rechten Seite des Fensters klicken
Los geht’s! Ein Chrome-Fenster öffnet sich und ihr seht etwas wie das Folgende:

Darin steckt so viel Großartiges:
- Es funktioniert einfach
- Cypress hat 120 (!) End-to-End-Tests gegen https://example.cypress.io in nur 76 Sekunden ausgeführt
- Man kann in jeden Testfall bis zu jedem einzelnen Schritt hineinzoomen und sehen, wie das DOM und auch der Bildschirm genau in diesem Moment aussahen:
- Keine Installation von WebDriver oder ähnlichem
- Live-Reloading aller Testskripte
- Es funktioniert einfach!

Wie funktioniert das?
Cypress hat den Vorteil, dass es vollständig anders aufgebaut ist als Selenium, wie Cypress-Gründer Brian Mann in diesem Video schön erklärt:
https://www.youtube.com/watch?v=lK_ihqnQQEM
Auf der Cypress-Homepage gibt es auch eine detaillierte Seite zur Funktionsweise, und der erste Punkt nennt genau diesen wesentlichen Vorteil:
Die meisten End-to-End-Testing-Tools basieren auf Selenium, weshalb sie alle dieselben Probleme teilen. Um Cypress anders zu machen, haben wir eine neue Architektur von Grund auf neu aufgebaut. Während Selenium Remote-Befehle über das Netzwerk ausführt, läuft Cypress in derselben Run-Loop wie eure Anwendung.
Da Cypress zusammen mit eurer Anwendung in derselben Loop läuft, können alle Vorteile genutzt werden, die mit webdriver-basierten Lösungen nicht möglich waren.
Aber es gibt noch viel mehr zu sagen – besonders liebe ich die asynchrone Unterstützung. Wer schon versucht hat, E2E-Tests für eine Single-Page-Application (z. B. Angular-basiert) zu automatisieren, weiß, wie umständlich es ist, auf clientseitige UI-Änderungen zu warten, die durch JavaScript ausgelöst werden. Thread.sleep() überall in den Testfällen – das braucht man mit Cypress nicht mehr.
TypeScript-Unterstützung hinzufügen
Wir wollen die Beispielskripte nicht verwenden, sondern eigene einfache Skripte gegen diesen Blog erstellen. Aber zunächst fügen wir TypeScript-Unterstützung zu unserem Projekt hinzu. Ich sagte ja, ich komme aus der Java-Welt, deshalb würde ich…
TypeScript hinzuzufügen ist ganz einfach – einfach eine tsconfig.json im Projektstamm erstellen:
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom", "es2019"],
"types": ["cypress", "node"],
"esModuleInterop": true
},
"include": ["**/*.ts"]
}
Und TypeScript zur package.json hinzufügen:
{
"name": "cypress-hashnode-sample",
"version": "1.0.0",
"description": "sample application to test cypress against hashnode",
"scripts": {
"cy:open": "cypress open"
},
"devDependencies": {
"cypress": "9.2.0",
"typescript": "^4.5.4"
}
}
Das war’s – wir können nun unsere Spezifikationsdateien typsicher implementieren.
Die Cypress-Konfigurationsdatei hinzufügen
Eine weitere wichtige Sache, die von Anfang an erstellt werden sollte, ist eine Cypress-Konfigurationsdatei namens cypress.json im Stammordner. Diese Datei kann später für alle möglichen Konfigurationseigenschaften verwendet werden, aber vorerst fügen wir nur diese zwei Eigenschaften hinzu:
{
"projectId": "cypress-hashnode-sample",
"baseUrl": "https://agilecoding.io"
}
Eine Testspezifikation erstellen
Jetzt ist es Zeit, unseren ersten Test zu erstellen. cypress/integration/blog-tests.ts mit folgendem Inhalt erstellen:
context('Blog tests', () => {
it('Open homepage', () => {
cy.visit('/');
});
})
Die Cypress-UI sollte dieses neue Skript erkannt haben und nun so aussehen:

Falls die UI versehentlich geschlossen wurde, erneut öffnen mit npm cy:open.
Den Test ausführen – eine spezielle Chrome-Instanz öffnet sich und ihr seht etwas wie das Folgende:

Jetzt fügen wir unserer Spezifikation unsere erste Assertion hinzu:
context('Blog tests', () => {
it('Open homepage', () => {
cy.visit('/');
cy.get('.blog-title').should('contain.text', "Agile Coding")
});
})
Die Tests werden sofort ausgeführt (Hot-Reloading), und das Ergebnis ist:

Jetzt fügen wir einen weiteren Testfall hinzu, geben Text ein und suchen nach einem Artikel:
it("Search article", () => {
cy.visit('/');
cy.get('[data-title="Search"]').click();
cy.get('input[placeholder*="Type"]').type("Hello");
cy.get('input[placeholder*="Type"]').parent()
.siblings()
.children('a')
.first()
.click();
cy.get('[data-query="post-title"]')
.should('have.text', 'Hello AgileCoding.io');
})
Dieser Test klickt auf das Suchsymbol oben rechts, gibt den Text „Hello“ ein, klickt auf die Suche und klickt dann auf den ersten Link der Ergebnisliste. Danach wird überprüft, dass der Titel der resultierenden Seite „Hello AgileCoding.io” lautet.
Zu beachten ist, dass die Art und Weise, wie wir hier mit .siblings() und .children auf die Ergebnisliste zugreifen, keine Best Practice ist, da dieser Test dann stark vom DOM der Seite abhängt. Wenn ihr Zugang zum DOM eurer zu testenden Seite habt, verwendet Data-Attribute, wie in diesem offiziellen Leitfaden beschrieben.
Da wir das DOM von Hashnode nicht ändern können, führen wir den Test aus:

Er besteht – großartig. Wer sich für das gesamte Projekt interessiert, schaut sich meinen GitHub-Account an.
CI-Integration
Nachdem wir gesehen haben, dass lokal auf unseren Entwicklungsmaschinen alles gut funktioniert, fragt ihr euch vielleicht: Und was ist mit Continuous Integration? Vielleicht möchtet ihr eure Tests regelmäßig auf einem zentralen Server ausführen, und ihr wisst, wie schwierig die Einrichtung mit webdriver-basierten Technologien wie Selenium ist.
Glücklicherweise gibt es auch hier mit Cypress zwei großartige Optionen:
- Das Cypress Dashboard: Das ist im Grunde das Geschäftsmodell von Cypress – eine problemlose Webplattform, auf der alle Test-Runs einschließlich Videos und Screenshots überprüft werden können, ohne eine eigene Infrastruktur aufzusetzen. Es ist ein klassisches SaaS-Produkt und wirklich großartig.
- Die Open-Source-Alternative Sorry Cypress: Diese erfordert eine eigene Infrastruktur und etwas Geduld bei der Einrichtung, aber einmal erledigt, können alle Tests auch auf dem lokalen CI-Server ausgeführt werden (das haben wir bei Cloudflight gemacht).
- Currents.dev: Führt Sorry Cypress in einer verwalteten Umgebung für euch aus.
Zusammenfassung
Es scheint, als hätte ich meinen heiligen Gral des End-to-End-Testings für Webanwendungen gefunden. Cypress ist wirklich ein Game-Changer und ermöglicht es uns sogar, Test-Driven Development zu betreiben – das heißt, wir reproduzieren Frontend-Bugs mit Cypress, bevor wir sie beheben.
Cypress ist wirklich ein Game-Changer und ermöglicht es uns, End-to-End-Testing in unsere täglichen Abläufe zu integrieren.
Es gäbe viel über die Details zu sagen, aber nochmals: Nicht nur Cypress selbst ist großartig, es hat auch eine tolle Dokumentation und fantastische Tutorials – taucht also einfach in diese Ressourcen ein.
Danke, Cypress, dass ihr mich endlich zum Lächeln bringt, wenn es um E2E geht.
Agent Hub
Gehen Sie über vereinzelte AI-Piloten hinaus und schaffen Sie eine sichere, skalierbare Grundlage für Agentic AI in Ihrer gesamten Organisation.
Zur Demo




