In der heutigen, schnelllebigen Softwareentwicklung ist eine effiziente und zuverlässige Testautomatisierung unverzichtbar, um qualitativ hochwertige Anwendungen pünktlich auszuliefern. Cypress hat sich als eines der leistungsstärksten Tools für End-to-End-Tests etabliert und bietet Entwicklern und Testern eine benutzerfreundliche Plattform zur Automatisierung ihrer Tests. Doch der Erfolg bei der Nutzung von Cypress hängt entscheidend davon ab, wie gut das Tool implementiert und in die bestehenden Prozesse integriert wird.
In diesem Artikel teilen wir unsere wichtigsten Erkenntnisse und bewährte Methoden aus der Praxis, die Ihnen helfen, das volle Potenzial von Cypress auszuschöpfen. Egal ob Sie gerade erst in die Welt von Cypress einsteigen oder bereits Erfahrung mit dem Tool gesammelt haben – die hier vorgestellten Best Practices bieten praktische Ansätze, um Ihre Testprozesse zu verbessern
Transparente Testprozesse durch JIRA-Anbindung
Eine der größten Herausforderungen bei der Testautomatisierung ist die lückenlose Nachverfolgbarkeit der Testfälle und deren direkte Verknüpfung mit den User Stories und Anforderungen in JIRA. Ohne eine effektive Integration kann es schwierig sein, den Überblick über den Teststatus zu behalten und sicherzustellen, dass alle Akzeptanzkriterien abgedeckt sind. Hier hat sich das cypress-xray-plugin als äußerst nützlich erwiesen.
Was ist das cypress-xray-plugin?
Das cypress-xray-plugin ermöglicht die Integration von Cypress-Tests mit Xray, einem Testmanagement-Tool für JIRA. Mit diesem Plugin können Testergebnisse automatisch an JIRA übermittelt werden, wodurch der Teststatus in Echtzeit sichtbar wird. Dies fördert nicht nur die Transparenz, sondern verbessert auch die Zusammenarbeit zwischen Entwicklungs- und Testteams.
Durch die Verwendung des cypress-xray-plugin können wir:
-
Effiziente Validierung der Akzeptanzkriterien: Jeder Testfall kann direkt mit einer User Story oder Anforderung in JIRA verknüpft werden, sodass klar ersichtlich ist, welche Anforderungen abgedeckt sind.
-
Echtzeit-Teststatus in JIRA: Der aktuelle Status jedes Tests (bestanden, fehlgeschlagen, übersprungen) wird automatisch in JIRA aktualisiert.
-
Verbesserte Organisation des Testprozesses: Die zentrale Verwaltung von Testfällen und Ergebnissen in JIRA erleichtert die Planung und Durchführung von Tests.
Mit dieser JIRA-Anbindung ist sofort erkennbar, welcher Testfall bestanden oder fehlgeschlagen ist. Dies erhöht die Übersichtlichkeit und reduziert den manuellen Aufwand bei der Dokumentation der Testergebnisse erheblich.
Beispiel-Testfall
Erstellen wir einen einfachen Cypress-Test und verknüpfen ihn mit einem JIRA-Testfall:
describe('Login-Funktionalität', () => { it('XRAY-1 Benutzer kann sich mit gültigen Anmeldeinformationen anmelden', () => { cy.visit('https://example.com/login'); cy.get('#username').type('testuser'); cy.get('#password').type('password123'); cy.get('button[type="submit"]').click(); cy.url().should('include', '/dashboard'); }); });
In diesem Beispiel ist XRAY-1 der Schlüssel des Testfalls in JIRA. Durch die Einbindung des Schlüssels im Testnamen wird der Test direkt mit dem entsprechenden JIRA-Testfall verknüpft.
Vermeidung von Testabhängigkeiten: Eigenständige User für stabile Tests
Ein häufiges Problem bei der Testautomatisierung ist die Abhängigkeit zwischen Testfällen. Wenn Tests voneinander abhängig sind, kann ein Fehler in einem Testfall eine Kaskade von Fehlschlägen in nachfolgenden Tests verursachen. Dies beeinträchtigt die Stabilität, Zuverlässigkeit und Wiederholbarkeit der Tests. Um dieses Problem zu vermeiden, ist es entscheidend, dass jeder Testfall isoliert ist und das System unabhängig von anderen Tests beeinflusst und anschließend den Ausgangszustand wiederherstellt.
Die Herausforderung von abhängigen Testfällen
- Unvorhersehbare Testergebnisse: Wenn ein Test den Zustand für einen anderen Test ändert, kann dies zu unerwarteten Ergebnissen führen.
- Schwierige Fehleranalyse: Es wird komplizierter, die Ursache eines Fehlers zu identifizieren, da mehrere Tests involviert sein können.
- Geringere Testgeschwindigkeit: Abhängigkeiten können die Ausführungszeit erhöhen, da Tests in einer bestimmten Reihenfolge laufen müssen.
Unsere Lösung: Eigenständige Test-User mit unterschiedlichen Rollen
Um die Unabhängigkeit der Tests zu gewährleisten, haben wir beschlossen, dedizierte Test-User zu erstellen, die jeweils über spezifische Rollen verfügen (z. B. Admin, einfacher User, etc.). Dies ermöglicht es uns, Tests zu schreiben, die:
- Isoliert sind und keine Seiteneffekte auf andere Tests haben.
- Robust sind, da sie nicht von vorherigen Testausführungen abhängen.
- Wiederholbar sind, unabhängig von der Reihenfolge, in der sie ausgeführt werden.
Hierfür noch ein kleines Beispiel:
Der folgende Cypress-Test prüft, ob ein Admin-Benutzer eine Seite temporär offline nehmen kann. Der Test meldet zunächst einen Admin-Test-User an, führt die entsprechende Aktion durch und verifiziert, dass die Seite tatsächlich nicht mehr erreichbar ist. Nach dem Test wird die Seite wieder online gestellt, um den Ausgangszustand wiederherzustellen.
describe('Admin-Funktionalität: Seite offline nehmen', () => { before(() => { cy.fixture('users').then((users) => { cy.loginTestUser(users.admin); }); }); it('Admin kann eine Seite temporär offline nehmen', () => { cy.visit('/admin/dashboard'); // Aktion: Seite offline nehmen cy.get('#managePages').click(); cy.get('#pageList') .contains('Kontaktseite') .parent() .find('.toggleOffline') .click(); // Überprüfung: Seite ist offline cy.visit('/kontakt'); cy.contains('Diese Seite ist momentan nicht verfügbar').should('be.visible'); }); after(() => { // Aufräumen: Seite wieder online stellen und Admin-User löschen cy.visit('/admin/dashboard'); cy.get('#managePages').click(); cy.get('#pageList') .contains('Kontaktseite') .parent() .find('.toggleOffline') .click(); }); });
Unabhängig von Designänderungen: Stabilere Tests mit Test-IDs
Automatisierte Tests verlassen sich oft auf CSS-Selektoren, um Elemente auf einer Seite zu identifizieren. Das Problem dabei: CSS-Selektoren sind für Styling gedacht, nicht für funktionale Tests. Ändert sich das Styling, können Tests instabil werden und fehlschlagen. Unsere Lösung: dedizierte Test-IDs, die nur für Tests erstellt werden und sich auf die Funktion des Codes beziehen, nicht auf das Design.
Test-IDs haben folgende Vorteile:
-
Bessere Wartbarkeit: Tests sind einfacher zu pflegen, da Änderungen am CSS keinen Einfluss haben.
-
Stabile Tests: Änderungen am CSS beeinträchtigen die Tests nicht.
-
Klare Trennung: Testlogik bleibt unabhängig vom Design.
Mit Test-IDs verhindern wir, dass Tests fehlschlagen, wenn Änderungen an der Component Library oder am CSS vorgenommen werden. Dadurch bleibt die Testumgebung langfristig stabil und zuverlässig.
Beispiel
Ein Button dient zum Absenden eines Login-Formulars. Er hat die CSS-Klasse „primary-button“, die den Button blau färbt. Diese Klasse spiegelt jedoch nicht die Funktion wider. Ändert sich die Farbe des Buttons (etwa zur Sekundärfarbe), würde ein auf „primary-button“ basierter Test fehlschlagen. Mit einer Test-ID jedoch bleibt der Test robust.
<button class="primary-button" data-testid="submit-login-button">Submit</button>
Implementieren von Custom Commands
Das DRY-Prinzip (Don’t Repeat Yourself) ist auch in der Testautomatisierung von zentraler Bedeutung. Cypress erlaubt die Erstellung von benutzerdefinierten Commands, die wiederkehrende Aufgaben standardisieren und vereinfachen. Dies reduziert den Codeumfang und erleichtert die Wartung.
Beispiel für einen Custom Command:
Ein wiederkehrendes Test-Szenario ist das Ändern von Dropdown-Menüs. Um die Wiederholung dieses Codes zu vermeiden, haben wir einen Custom Command für diese Aktion entwickelt, insbesondere bei selbst entwickelten Komponenten. Auf diese Weise konnten wir häufig verwendete Aktionen effizient zusammenfassen und unseren Code übersichtlicher gestalten.
So könnte beispielsweise ein Custom Command für eine Dropdown aussehen:
// cypress/support/commands.js // Custom Command to select an option from a dropdown using data-testid attributes for both dropdown and option Cypress.Commands.add('selectDropdownOptionByTestId', (dropdownTestId, optionValueTestId) => { // Öffnet das Dropdown-Menü anhand seines data-testid Attributs cy.get(`[data-testid="${dropdownTestId}"]`).click(); // Wählt die spezifische Option im Dropdown anhand ihres data-testid und klickt sie an cy.get(`[data-testid="${optionValueTestId}"]`).click(); });
Angenommen, die HTML-Struktur sieht folgendermaßen aus…
<select data-testid="colorDropdown"> <option value="RED" data-testid="option-RED">Rot</option> <option value="BLUE" data-testid="option-BLUE">Blau</option> <option value="GREEN" data-testid="option-GREEN">Grün</option> </select>
…dann könnte man das Dropdown mit dem Custom Command im Testfall auswählen
// Nutzung des Custom Commands im Test describe('Dropdown Selection Test', () => { it('should select the color RED from the dropdown using data-testid', () => { cy.visit('https://example.com'); // URL zur Testseite cy.selectDropdownOptionByTestId('colorDropdown', 'option-RED'); // Wählt die Option "Rot" (value: RED) // Optional: Assertion, um sicherzustellen, dass die richtige Option ausgewählt wurde cy.get(`[data-testid="colorDropdown"]`).should('have.value', 'RED'); }); });
Setzen von Pre-Conditions vor jedem Test
Stabile Tests benötigen eine kontrollierte Testumgebung. Dies erfordert, dass vor jedem Test bestimmte Bedingungen, sogenannte Pre-Conditions, erfüllt sind. Hierzu haben wir eine Routine erstellt, die sicherstellt, dass Authentifizierung und User-Konfiguration vor jedem Test ausgeführt werden.
Ein Beispiel für Pre-Conditions:
- Authentifizierung eines Users
- Setzen der Anwendungssprache (besonders relevant, um die Anwendung in unterschiedlichen Übersetzungen zu testen)
- Entfernen von Cookie-Bannern beim Start der Anwendung
- Setzen von bestimmten Einstellungen im localStorage
Diese Pre-Conditions haben wir ebenfalls mithilfe von Custom Commands implementiert, was die Wiederverwendbarkeit und Stabilität unserer Tests erhöht.
Hier ein Beispiel für einen Custom Command für das Setzen von Pre Conditions:
Cypress.Commands.add( 'setupPreConditions', ({ userRole = 'DEFAULT', lang = 'EN', }: Partial<{ userRole: 'DEFAULT' | 'GUEST' | 'ADMIN'; lang: 'EN' | 'DE' | 'IT'; }> = {}) => { // Setzt eine Umgebungsvariable für den Benutzer Cypress.env('currentUser', userRole); // Führt Login durch cy.performLogin(userRole); // Setzt die Sprache cy.setLanguage(lang) // Führt UI-Einstellungen durch cy.dismissCookieBanner(); }, );
Die Methode setupPreConditions kann dann vor jedem Testfall aufgerufen werden
describe('Test Suite Name', () => { beforeEach(() => { cy.setupTestPreConditions({ userRole: 'DEFAULT', // z. B. 'GUEST' oder 'ADMIN' für andere Rollen lang: 'test12345', // z. B. 'DE' für die deutsche Sprache }); }); it('should perform a specific test case', () => { // Testfall-Logik }); it('should perform another test case', () => { // Ein weiterer Testfall }); });
Fazit
Durch die Anwendung dieser Best Practices konnten wir unsere Cypress-Tests stabiler, wartbarer und effizienter gestalten. Der Einsatz von JIRA-Integrationen, dedizierten Test-IDs, Custom Commands und gezielten Pre-Conditions ermöglicht uns eine schnelle und zuverlässige Testautomatisierung. Diese Maßnahmen tragen entscheidend dazu bei, die Qualität und Effizienz unserer Entwicklungsprozesse kontinuierlich zu verbessern.


