Lassen Sie uns für End-to-End-Tests in Cypress eintauchen
Veröffentlicht: 2022-03-10Dieser Artikel wurde von unseren lieben Freunden bei LambdaTest gesponsert, die das browserübergreifende Testerlebnis für so viele Menschen auf der ganzen Welt reibungsloser machen. Danke!
Softwareentwicklung ohne automatisiertes Testen ist heute kaum noch vorstellbar. Eine gute Auswahl an unterschiedlichen Testverfahren sichert ein hohes Qualitätsniveau. Als Grundlage für das Testen können wir eine Reihe von Unit-Tests verwenden. Obendrein, sozusagen in der Mitte der Pyramide, befinden sich Integrationstests. End-to-End-Tests stehen ganz oben und decken die kritischsten Anwendungsfälle ab. Diese dritte Art von Tests steht im Mittelpunkt dieses Artikels.
End-to-End-Tests haben jedoch einige Fallstricke , die Anlass zur Sorge geben:
- End-to-End-Tests sind langsam und stellen daher eine erhebliche Hürde in jeder Strategie für Continuous Integration und Continuous Deployment (CI/CD) dar. Nicht nur das, sondern stellen Sie sich vor, eine Aufgabe, ein Feature oder eine andere Implementierung abzuschließen – das Warten auf die Ausführung des Tests kann die Geduld aller erschöpfen.
- Solche End-to-End-Tests sind aufgrund des Debugging-Aufwands schwer zu warten, fehleranfällig und in jeder Hinsicht teuer. Verschiedene Faktoren können dies verursachen. Ihr Test sollte sich wie ein Assistent anfühlen, niemals ein Hindernis.
- Der größte Alptraum für Entwickler ist ein flockiger Test, also ein Test, der auf die gleiche Weise ausgeführt wird, aber zu unterschiedlichen Ergebnissen führt. Das ist wie ein „Heisenbug“, der nur auftritt, wenn man die zu testende Anwendung nicht misst – also nicht anschaut.
Aber keine Sorge: Diesen Fallstricken müssen Sie nicht erliegen. Schauen wir uns an, wie viele von ihnen verhindert werden können . Ich werde jedoch nicht nur den Mond versprechen und nicht liefern. In diesem Leitfaden schreiben wir gemeinsam einige Tests, die ich für Sie in einem GitHub-Repository veröffentlicht habe. Auf diese Weise hoffe ich, Ihnen zu zeigen, dass End-End-Testen Spaß machen kann! Lass uns anfangen.
Was sind End-to-End-Tests?
Wenn ich über End-to-End (oder E2E)-Tests spreche, bezeichne ich es gerne als „workflow-basiert“. Der Begriff fasst End-to-End-Testing gut zusammen: Es simuliert tatsächliche Benutzerabläufe und sollte möglichst viele Funktionsbereiche und Teile des in der Anwendung verwendeten Technologie-Stacks umfassen. Am Ende gibt sich der Computer als Kunde aus und versucht, sich wie ein echter Benutzer zu verhalten. Diese Tests eignen sich am besten, um das gesamte System Ihrer Anwendung konstant zu belasten, und sind daher eine hervorragende Maßnahme, um die Qualität sicherzustellen, wenn der gesamte Anwendungsstapel vorhanden ist.
Erinnern wir uns, was wir mit all dem erreichen wollen. Wir wissen, dass Front-End-Tests eine Reihe von Praktiken zum Testen der Benutzeroberfläche einer Webanwendung, einschließlich ihrer Funktionalität, sind. Macht Sinn – mit diesen Maßnahmen können wir sicherstellen, dass unsere Anwendung korrekt funktioniert und dass keine zukünftigen Änderungen unseren Code beschädigen. Um dies effizient zu erreichen, fragen Sie sich vielleicht, was und wie viel Sie testen müssen.
Dies ist eine berechtigte Frage. Eine mögliche Antwort finden Sie vielleicht in einer Metapher: Die Testautomatisierungspyramide, zuerst eingeführt von Mike Cohn und weiter spezifiziert von Martin Fowler, zeigt, wie man Tests effizient gestaltet . Auf der untersten Pyramidenebene finden wir schnelle und günstige Unit-Tests, ganz oben zeitraubende und teure UI-Tests (End-to-End-Testing).
Dies und seine Vor- und Nachteile zu erläutern, würde für einen eigenen Artikel ausreichen. Ich möchte mich auf eine Ebene konzentrieren. Insbesondere End-to-End-Tests können bei effizienter Priorisierung deutliche Qualitätsverbesserungen bringen. Auf diese Weise können wir unser System ständig belasten und sicherstellen, dass die Hauptfunktionen unserer Anwendung ordnungsgemäß funktionieren.
Meine Reise nach Cypress
Als ich anfing zu lernen, wie man End-to-End-Tests schreibt, verwendete ich Mink, eine PHP-Bibliothek, zusätzlich zu Behat, einem szenarioorientierten, verhaltensgesteuerten Entwicklungsframework (BDD). Ich fing an, Selen zu verwenden, mit all seinen Vor- und Nachteilen. Da mein Team begonnen hatte, viel mit Vue.js zu arbeiten, wechselten wir zu einem JavaScript-basierten Testframework, um eine einwandfreie Integration und Kompatibilität sicherzustellen. Unsere Wahl fiel damals auf Nightwatch.js, also habe ich unsere neue Testsuite von Grund auf neu erstellt.
In dieser Zeit sind wir oft auf Kompatibilitätsprobleme gestoßen . Man könnte es Abhängigkeitshölle nennen – ganz zu schweigen von all den Einschränkungen, die wir bei Selenium und später bei WebDriver gesehen haben.
- In unserem Team konnten wir die Chrome-Version unseres CI nicht bestimmen. Wenn also Updates für Chrome veröffentlicht wurden, war Nightwatch.js nicht schnell genug, um kompatibel zu sein, was viele Fehler in unseren Testpipelines verursachte.
- Die Zahl der testseitigen Ursachen für flockige Tests begann zu steigen, da die Wartemöglichkeiten von Nightwatch.js nicht optimal zu unserem Produkt passten.
Also kamen wir auf den Gedanken, unsere Testsuite neu zu erstellen. Nachdem ich eine Unkonferenz besucht hatte, entdeckte ich Cypress.
Cypress ist ein All-in-One-Testframework, das Selenium oder WebDriver nicht verwendet. Das Tool verwendet Node.js, um einen Browser unter besonderer Kontrolle zu starten. Die Tests in diesem Framework werden auf Browserebene ausgeführt und nicht nur ferngesteuert. Das bietet mehrere Vorteile.
Kurz gesagt, hier sind die Gründe, warum ich mich für dieses Framework entschieden habe:
- Hervorragende Debugging-Fähigkeit
Der Testrunner von Cypress kann über Snapshots zu jedem Status der Anwendung zurückspringen. So können wir direkt einen Fehler und alle Schritte davor sehen. Darüber hinaus gibt es vollen Zugriff auf die Entwicklertools (DevTools) von Chrome, und Klicks werden vollständig aufgezeichnet. - Bessere Möglichkeiten zum Warten auf Aktionen im Test oder in der Benutzeroberfläche oder in den Antworten von der API
Cypress bringt implizites Warten mit sich, sodass keine entsprechenden Überprüfungen erforderlich sind. Sie können den Test auch auf Animationen und API-Antworten warten lassen. - Tests werden in JavaScript geschrieben
Dies mildert die Lernkurve zum Schreiben von Tests. Der Testrunner von Cypress ist Open Source und passt daher zu unserer Produktstrategie.
Dieser Artikel ist jedoch ein Leitfaden, also lassen Sie uns mit diesen allgemeinen Informationen aufhören und loslegen.
Einstieg
Cypress installieren und starten
Fangen wir bei Null an. In meinen Vorträgen über Cypress beginne ich normalerweise mit dem Erstellen eines neuen Verzeichnisses über mkdir
und installiere dann sofort Cypress. Die einfachste Art der Installation ist in dieser Zeichnung dargestellt:
Kleiner Tipp: Wer npm nicht nutzen möchte, kann Cypress via Yarn installieren:
yarn add cypress --dev
Eine Alternative ist der direkte Download über die von Cypress bereitgestellten ZIP-Ordner. Das ist es! Sobald die Installation abgeschlossen ist, können Sie beginnen.
Es gibt zwei Möglichkeiten, mit der Ausführung von Cypress-Tests zu beginnen. Die erste besteht darin, Cypress in der Konsole zu starten und Ihre Tests kopflos auszuführen:
./node_modules/.bin/cypress run
Die zweite Möglichkeit besteht darin, eine der netten Funktionen von Cypress zu verwenden, nämlich den integrierten Testrunner. Der Test Runner ist eine Benutzeroberfläche zum Ausführen von Tests. Um es zu starten, können Sie einen ähnlichen Befehl verwenden:
./node_modules/.bin/cypress open
Dieser Befehl öffnet den Testrunner. Wenn Sie Cypress zum ersten Mal öffnen, sehen Sie diese Oberfläche:
Cypress bietet einige vorgefertigte Beispieltests, um seine Funktionen zu demonstrieren und Ihnen einige Ausgangspunkte zu geben – das ist der Grund für die verfügbaren Tests. Lassen Sie uns diese vorerst ignorieren, da wir bald unsere eigenen schreiben wollen. Behalten Sie jedoch bitte diesen Bereich „Integrationstests“ im Hinterkopf, da er für einen Großteil der Magie verantwortlich ist, die später geschehen wird.
Erster Eindruck von Cypress' Struktur
Jetzt ist es an der Zeit, unser neu erstelltes Projekt in der integrierten Entwicklungsumgebung (IDE) Ihrer Wahl zu öffnen. Wenn Sie zu diesem Ordner navigieren, sehen Sie die folgende Teststruktur:
smashing-example └── cypress └── fixtures └── integration └── plugins └── support └── cypress.json
Gehen wir diese Ordner durch:
-
fixtures
Hier finden Sie feste Testdaten, die keinen Bezug zu den anderen Entitäten haben. Hier werden also keine IDs gespeichert, die sich je nach Bundesland ändern können. -
integration
Die aktuellen Tests finden Sie hier. -
plugins
Hier können Sie Cypress erweitern, sei es mit bestehenden Cypress-Plugins oder Ihren eigenen. -
support
Hier können Sie Cypress selbst erweitern. Ihre eigenen Befehle und Helfer befinden sich hier. -
cypress.json
Ändern Sie hier Konfigurationen, auch für die Umgebung.
In Ordnung, ich denke, wir können uns jetzt in Cypress zurechtfinden, ob im Testrunner oder im Quellcode. Aber wie fangen wir an? Was wollen wir testen?
Wählen Sie einen Testfall aus
Ein typischer End-to-End-Test kann komplex werden, insbesondere wenn er viele Schritte umfasst. Die manuelle Ausführung würde viel Zeit in Anspruch nehmen. Aufgrund dieser Komplexität können E2E-Tests schwierig zu automatisieren und langsam in der Ausführung sein. Daher müssen wir sorgfältig entscheiden, welche Fälle automatisiert werden sollen.
Meiner Meinung nach ist der Begriff „workflowbasiert“ entscheidend : Wir würden Testfälle basierend auf typischen User Stories auswählen. Aufgrund von Laufzeiten ist es jedoch nicht ratsam, jeden einzelnen verfügbaren Workflow abzudecken. Daher brauchen wir eine Möglichkeit, unsere Testfälle zu priorisieren.
In meinem Team hatten wir mehrere Kriterien für unser Projekt. Der Testfall sollte:
- decken die allgemeinsten und am häufigsten verwendeten Arbeitsabläufe einer Funktion ab, wie z. B. CRUD-Vorgänge (der Begriff „glücklicher Pfad“ beschreibt diese Arbeitsabläufe ziemlich gut);
- Verwenden Sie eine Risikoanalyse, die die Arbeitsabläufe mit E2E-Tests abdeckt, die am anfälligsten sind (dh wo Fehler den größten Schaden anrichten würden);
- Vermeidung von Doppeldeckungen;
- nicht unbedingt verwendet werden, wenn Komponententests geeigneter sind (verwenden Sie einen E2E-Test, um die Reaktion Ihrer Software auf einen Fehler zu testen, nicht den Fehler selbst).
Die zweitwichtigste Sache, die Sie beachten sollten, ist, nur den Workflow zu testen, den Sie ausdrücklich testen möchten. Alle anderen Schritte, die erforderlich sind, damit Ihr Test funktioniert, sollten mit API-Operationen außerhalb des Tests durchgeführt werden, um zu vermeiden, dass sie getestet werden. Auf diese Weise stellen Sie minimale Testlaufzeiten sicher und erhalten ein eindeutiges Ergebnis Ihres Testfalls, wenn dieser fehlschlägt. Stellen Sie sich diesen Workflow so vor, wie es ein Endbenutzer tun würde: Konzentrieren Sie sich auf die Verwendung der Funktion und nicht auf die technische Implementierung .
Beispiel:
Wenn Sie den Checkout-Prozess in einem Online-Shop testen möchten, verzichten Sie auf alle anderen Schritte, wie z. B. das Anlegen der Produkte und Kategorien, auch wenn Sie diese für die Abwicklung des Checkouts benötigen. Verwenden Sie zum Beispiel eine API oder einen Datenbank-Dump, um diese Dinge zu machen, und konfigurieren Sie den Test nur für den Checkout.
Beispiel: Meine Artikel im Smashing Magazine finden
Ich möchte einen Test für diese Website, Smashing Magazine, schreiben. Ich kann nicht garantieren, dass dieser Test für immer aktuell ist, aber hoffen wir, dass er Bestand hat. In jedem Fall finden Sie dieses Beispiel in einem GitHub-Repository.
Erstellung unseres ersten Cypress-Tests
Im integration
erstellen wir zunächst eine neue Datei. Nennen wir es find-author.spec.js
. Die Endung .spec
steht für „Spezifikation“. In Bezug auf einen Test bezieht sich dies auf die technischen Details einer bestimmten Funktion oder Anwendung, die Ihre Anwendung erfüllen muss.
Um diese leere JavaScript-Datei in das Zuhause eines Tests zu verwandeln, beginnen wir damit, der Testsuite ihre Struktur zu geben. Wir verwenden die Methode " describe
. describe()
oder context()
wird verwendet, um die Tests zu enthalten und zu organisieren. Mit anderen Worten, diese Methode dient als Rahmen für unsere Tests. Unsere Testdatei sieht also so aus:
// find-author.spec.js describe('Find authors at smashing', () => { //... });
Im nächsten Schritt wird der eigentliche Test erstellt. Wir verwenden die Methode it
. it()
oder specify()
wird verwendet, um den eigentlichen Test darzustellen. Wie Sie sehen, können wir mehrere Tests in einer Datei erfassen, was einige hervorragende Strukturierungsmöglichkeiten bietet.
// find-author.spec.js describe('Find authors at smashing', () => { it('Find the author Ramona Schwering', () => { cy.log('This is our brand-new test'); }); });
Kleiner Tipp : Wenn Sie mit Mokka vertraut sind, sind Ihnen vielleicht einige Ähnlichkeiten aufgefallen. Cypress baut auf Mocha auf, daher ist die Syntax dieselbe.
In Ordnung, machen wir weiter. Wenn wir unseren Test im Test Runner von Cypress ausführen, werden wir feststellen, dass Cypress einen Browser öffnet, um den Test auszuführen. Dieser Browser ist im folgenden Screenshot zu sehen:
Glückwünsche! Wir haben unseren ersten Test geschrieben! Klar bringt das nicht viel. Wir müssen weitermachen. Füllen wir unseren Test mit Leben.
Füllen Sie den Test mit Leben
Was ist das erste, was zu tun ist, wenn eine Website getestet wird? Richtig, wir müssen die Website öffnen. Wir können das mit einem Cypress-Befehl tun. Was ist der Befehl, fragen Sie sich vielleicht?
Arbeiten mit Befehlen
Es gibt hauptsächlich zwei Arten von Anweisungen, die in einem E2E-Test verwendet werden. Die erste Art von Anweisungen, die Befehle, repräsentieren die einzelnen Schritte im Test. Im Kontext von Cypress sind Befehle alles, was Cypress tut, um mit Ihrer Website zu interagieren. Diese Interaktion kann alles sein – ein Klick, das Herunterscrollen der Website oder sogar das Finden eines Elements. Infolgedessen werden Befehle eines der wichtigen Dinge sein, mit denen wir unseren Test füllen werden.
Unser erster Befehl wird also derjenige sein, der zur Website navigiert – smashingmagazine.com
. Dieser Befehl heißt visit
.
Damit sieht unser Test so aus:
// find-author.spec.js describe('Find authors at smashing', () => { it('Find the author Ramona Schwering', () => { cy.visit('https://www.smashingmagazine.com/'); }); });
Es gibt einen Befehl, den ich oft benutze – und Sie werden es auch tun. Es heißt get
:
cy.get('selector');
Dieser Befehl gibt ein Element entsprechend seinem Selektor zurück – ähnlich wie $(…)
von jQuery. Sie würden also diesen Befehl verwenden, um die Teile zu finden, mit denen Sie interagieren möchten. Normalerweise würden Sie es verwenden, um eine Befehlskette zu starten. Aber warten Sie – was ist mit einer Befehlskette gemeint?
Wie am Anfang dieses Artikels erwähnt, sind alle Tests und alles, was dazugehört, in JavaScript geschrieben. Sie können die Befehle in den Tests (dh die Anweisungen) in einer Kette (mit anderen Worten verkettet) platzieren. Das bedeutet, dass die Befehle einen Betreff (oder Rückgabewert) eines Befehls an den folgenden Befehl weitergeben können, wie wir es von vielen Testframeworks kennen.
In Ordnung, wir beginnen eine Befehlskette mit dem get
-Befehl. Um ein Element mit get
zu finden, müssen wir zuerst seinen Selektor finden. Es ist wichtig, einen eindeutigen Selektor zu finden, da Cypress sonst alle übereinstimmenden Elemente zurückgeben würde; Denken Sie also daran und vermeiden Sie es, wenn es unbeabsichtigt ist.
Interaktion mit Elementen
Cypress selbst hat eine Funktion, die Ihnen hilft, die Selektoren der Elemente zu finden, mit denen Sie arbeiten möchten. Diese Funktion wird als Selector Playground bezeichnet und hilft Ihnen, eindeutige Selektoren einer Komponente zu entdecken oder alle übereinstimmenden Elemente für einen Selektor oder eine Textzeichenfolge anzuzeigen. Diese Funktion kann Ihnen also bei dieser Aufgabe sehr helfen. Um es zu aktivieren, klicken Sie einfach auf das Fadenkreuzsymbol in der Kopfzeile der Benutzeroberfläche Ihres Tests und bewegen Sie dann den Mauszeiger über das gewünschte Element:
Wie im obigen Screenshot zu sehen, zeigt ein Tooltip den Selektor beim Hover oder in dieser kleinen Leiste unter dem Fadenkreuz-Symbol an, das erschien, wenn auf das Element geklickt wurde. In dieser Leiste können Sie auch sehen, wie viele Elemente mit dem angegebenen Selektor übereinstimmen würden, was in unserem Fall seine Eindeutigkeit sicherstellt.
Manchmal sind diese automatisch generierten Selektoren möglicherweise nicht die, die Sie verwenden möchten (z. B. wenn sie lang oder schwer lesbar sind oder Ihre anderen Kriterien nicht erfüllen). Der unten generierte Selektor ist meiner bescheidenen Meinung nach schwer zu verstehen und zu lang:
In diesem Fall würde ich auf die DevTools des Browsers zurückgreifen, um meine eindeutigen Selektoren zu finden. Sie sind vielleicht mit diesen Tools vertraut; In meinem Fall wähle ich für diesen Zweck oft Chrome. Andere unterstützte Browser bieten jedoch möglicherweise ähnliche Funktionen. Der Vorgang ähnelt dem Selector Playground, außer dass wir die DevTools-Funktionen auf der Registerkarte „Element“ verwenden.
Um sicherzustellen, dass ein Selektor eindeutig ist, empfehle ich, in der Codeansicht Ihrer DevTools danach zu suchen. Wenn Sie nur ein Ergebnis finden, können Sie sicher sein, dass es einzigartig ist.
Wussten Sie, dass es viele verschiedene Selektortypen gibt ? Je nach Sorte können Tests ziemlich unterschiedlich aussehen und sich sogar ganz anders verhalten. Einige Sorten eignen sich besser für End-to-End-Tests als andere. Wenn Sie wissen möchten, welche Selektoren Sie verwenden müssen, um Ihre Tests stabil und sauber zu halten, kann ich Sie auf einen meiner Artikel verweisen, der dieses Problem behandelt. Die Entwickler von Cypress selbst geben in ihren Best Practices einige Anleitungen zu diesem Thema.
Unser Test als Folge von Befehlen
OK, zurück zu unserem Test. Darin wollen wir unseren Workflow darstellen:
„Ich als Nutzer suche nach dem Artikel des Autors und navigiere über den Verweisbereich in einem seiner Artikel auf die Website des Autors.“
Wir reproduzieren die Schritte, die ein Benutzer mit Befehlen ausführen würde. Ich füge unten den fertigen Test mit Kommentaren ein, die die Schritte erläutern:
// find-author.spec.js it('Find the author Ramona Schwering', () => { // Open the website cy.visit('https://www.smashingmagazine.com'); // Enter author's name in search field cy.get('#js-search-input').type('Ramona Schwering'); // Navigate to author's article cy.get('h2 > a').first().click(); // Open the author's page cy.get('.author-post__author-title').click(); });
Dieses Beispiel befasst sich mit dem Workflow, den wir testen möchten. Cypress führt diesen Test aus. Ist es also an der Zeit, „Herzlichen Glückwunsch“ zu sagen? Haben wir endlich unseren ersten Test fertig geschrieben?
Nun, schauen Sie bitte genauer hin . Cypress wird es ausführen, aber es wird nur das tun, was der Test ihm sagt, was auch immer Sie geschrieben haben. Wenn Sie es im Test Runner ausführen, können Sie sehen, ob es bestanden hat – aber nicht, falls Sie es kopflos ausgeführt haben. Bei diesem Test wissen wir nur, ob Cypress unsere Befehle erfolgreich ausführen konnte – nicht, ob wir auf der Website des Autors gelandet sind. Also müssen wir unseren Test lehren, um das festzustellen.
Arbeiten mit Behauptungen
Der zweite Anweisungstyp kümmert sich um die Beschreibungen des gewünschten Zustands der Benutzeroberfläche – also ob etwas vorhanden, sichtbar oder nicht mehr sichtbar sein soll. Die Assertionen in Cypress basieren auf Chai- und Sinon-Chai-Assertionen, was sich in der Syntax bemerkbar macht.
Denken Sie daran, dass wir überprüfen möchten, ob wir uns auf der Profilseite des Autors befinden – in diesem Beispiel meiner. Also müssen wir genau dafür eine Behauptung hinzufügen:
// find-author.spec.js it('Find the author Ramona Schwering', () => { // Open the website cy.visit('https://www.smashingmagazine.com'); // Enter author's name in search field cy.get('#js-search-input').type('Ramona Schwering'); // Navigate to author's article cy.get('h2 > a').first().click(); // Open the author's page cy.get('.author-post__author-title').click(); // Check if we're on the author's site cy.contains('.author__title', 'Ramona Schwering').should('be.visible'); });
Okay, jetzt haben wir einen Test geschrieben, der Wert hat. Also, ja, herzlichen Glückwunsch zum Schreiben Ihres ersten Tests ... auch wenn er noch nicht perfekt ist.
Machen wir unseren Test hübsch
Auch wenn es uns gelungen ist, einen ersten aussagekräftigen Test zu schreiben und dabei das Kernkonzept gelernt zu haben, würde ich diesen noch nicht zusammenführen, wenn er in einem Pull-Request vorgeschlagen würde. Ein paar Dinge sind noch zu tun, damit es glänzt.
Lassen Sie sich Zeit
Cypress hat in fast jedem Befehl eine Wiederholungsoption eingebaut, sodass Sie nicht warten müssen, ob beispielsweise ein Element bereits vorhanden ist. Dies prüft jedoch nur, ob ein Element im DOM vorhanden ist, nicht mehr. Cypress kann nicht alles vorhersagen, was Ihre Anwendung tut, daher kann es zu Unregelmäßigkeiten kommen, wenn Sie sich ausschließlich darauf verlassen.
Was würde ein Benutzer tun, wenn er eine Website sehen möchte, die noch geladen wird? Sie würden höchstwahrscheinlich warten, bis einige Teile der Website sichtbar (also geladen) werden, und dann mit ihnen interagieren. In unserem Test wollen wir genau das nachahmen: Wir wollen auf Änderungen in der UI warten, bevor wir mit der Interaktion beginnen . In den meisten Fällen beschränken wir dieses Verhalten auf die Elemente, die wir benötigen, und verwenden daher Zusicherungen für diese Elemente.
Wie Sie sehen, müssen wir unseren Test mehrmals warten lassen. Allerdings ist es auch nicht gut, zu oft zu warten. Als Faustregel würde ich vorschlagen, eine Assertion zu verwenden, um zu überprüfen, ob das Element, mit dem interagiert werden soll, vollständig geladen wurde, als erster Schritt, um festzustellen, ob die zu testende Website geladen wurde.
Schauen wir uns exemplarisch einen solchen Teil unseres Tests an. Ich habe eine Behauptung hinzugefügt, um sicherzustellen, dass unsere Seite vollständig geladen wurde :
// find-author-assertions.spec.js // Open website cy.visit('https://www.smashingmagazine.com'); // Ensure site is fully loaded cy.get('.headline-content').should('be.visible'); // Enter author's name in the search field cy.get('#js-search-input').type('Ramona Schwering');
Fügen Sie weiterhin Behauptungen auf diese Weise an allen Stellen hinzu, an denen unsere Website Ladezeiten oder mehrere Elemente haben wird, die neu gerendert werden müssen. Die vollständige Testdatei finden Sie im entsprechenden Test im GitHub-Repository.
Um nicht in die Falle flockiger Tests zu tappen, möchte ich Ihnen noch einen letzten Hinweis geben: Verwenden Sie niemals feste Wartezeiten wie cy.wait(500)
oder ähnliches.
API-Antworten sind Ihre Freunde
Es gibt vor allem eine nette Wartemöglichkeit, die ich gerne in meinen Tests nutze. In Cypress ist es möglich, mit Netzwerkfunktionen zu arbeiten – eine weitere hilfreiche Möglichkeit, in Ihrer Anwendung zu warten, besteht darin , diese Funktionen zu verwenden, um mit Netzwerkanforderungen zu arbeiten . Auf diese Weise können Sie den Test auf eine erfolgreiche API-Antwort warten lassen.
Wenn wir uns als Beispiel an unseren Workflow erinnern, könnte ein Schritt eine API-Wartemöglichkeit gut nutzen. Ich denke an die Suche. Eine entsprechende User Story könnte wie folgt aussehen:
„Als Entwickler möchte ich sicherstellen, dass unsere Suchergebnisse vollständig geladen sind, damit kein Artikel mit älteren Ergebnissen unseren Test in die Irre führt.“
Wenden wir das auf unseren Test an. Zunächst müssen wir die Route definieren, auf die wir später warten wollen. Dazu können wir den intercept
Befehl verwenden. Ich würde nach der Anfrage suchen und die Daten liefern, die ich benötige – in diesem Fall die Suchergebnisse.
Um dieses Beispiel einfach zu halten, verwende ich einen Platzhalter für die URL. Danach verwende ich einen Alias, damit Cypress später mit dieser Route arbeiten kann.
// find-author-hooks.spec.js // Set the route to work with it('Find the author Ramona Schwering', () => { // Route to wait for later cy.intercept({ url: '*/indexes/smashingmagazine/*', method: 'POST' }).as('search'); // With this alias Cypress will find the request again //...
In Cypress werden zu Beginn des Tests alle definierten Routen angezeigt. Daher möchte ich diese intercept
auch an den Anfang meines Tests stellen.
Jetzt können wir diesen Routen-Alias in Zusicherungen verwenden. Der einfachste Weg, dies zu tun, wäre mit dem wait
-Befehl von Cypress, direkt mit dem zuvor erwähnten Alias. Die alleinige Verwendung dieses Befehls würde jedoch dazu führen, dass auf die Antwort gewartet wird, unabhängig von ihrem Ergebnis . Selbst Fehlercodes wie 400 oder 500 würden als bestanden gelten, während Ihre Anwendung höchstwahrscheinlich kaputt gehen würde. Daher würde ich empfehlen, eine weitere Behauptung wie diese hinzuzufügen:
// find-author-hooks.spec.js // Later: Assertion of the search request's status code cy.wait('@search') .its('response.statusCode').should('equal', 200);
Auf diese Weise können wir präzise auf die Daten, Änderungen usw. der Software warten, ohne Zeit zu verlieren oder Probleme zu bekommen, wenn die Anwendung stark belastet wird. Auch hier finden Sie die vollständige Beispieldatei in meinem GitHub-Repository.
Cypress konfigurieren
Ich habe ein kleines Detail ausgelassen. Wenn Sie sich das vollständige Testbeispiel genauer ansehen, unterscheidet es sich geringfügig von denen, die wir hier in diesem Handbuch verwendet haben.
// Cypress describe('Find author at smashing', () => { beforeEach(() => { // Open website cy.visit('https://www.smashingmagazine.com'); }); //...
Ich verwende nur einen Schrägstrich, um die Website des Smashing Magazine zu öffnen. Wie funktioniert das? Nun, wenn Sie diesen Befehl so verwenden, navigieren Sie zur baseUrl
unserer Tests. baseUrl
ist ein Konfigurationswert, der als Präfix für die URL des cy.visit()
oder cy.request()
verwendet werden kann. Unter anderem können wir diesen Wert in der Datei cypress.json
definieren. Für unseren Test setzen wir die baseUrl
wie folgt:
// cypress.json { "baseUrl": "https://www.smashingmagazine.com" }
Lobende Erwähnung: Haken
Bleibt noch ein Thema, das ich erwähnen möchte, auch wenn unser Beispieltest dafür nicht geeignet ist. Wie in anderen Testframeworks üblich, können wir über sogenannte Lifecycle Hooks definieren, was vor und nach unseren Tests passiert. Genauer gesagt, diese existieren, um Code vor oder nach einem oder allen Tests auszuführen:
// Cypress describe('Hooks', function() { before(() => { // Runs once before all tests }); after(() => { // Runs once after all tests }); beforeEach(() => { // Runs before each test }); afterEach(() => { // Runs after each test }); });
Wir möchten unsere Testdatei mit mehr als einem Test füllen, also sollten wir nach gemeinsamen Schritten suchen, die wir davor oder danach ausführen möchten. Unsere erste Zeile ist ein typisches Beispiel, nämlich der visit
. Angenommen, wir möchten diese Website vor jedem dieser Tests öffnen, würde ein beforeEach
Hook in unserem Beispiel so aussehen:
// Cypress describe('Find author at smashing', () => { beforeEach(() => { // Open website cy.visit('https://www.smashingmagazine.com'); }); //...
Ich nutze dies häufig in meiner täglichen Arbeit, um zum Beispiel dafür zu sorgen, dass meine Anwendung vor dem Test in den Grundzustand zurückgesetzt wird und somit den Test von anderen Tests isoliert. (Verlassen Sie sich niemals auf vorherige Tests! ) Führen Sie Ihre Tests isoliert voneinander aus, um die Kontrolle über den Zustand der Anwendung zu behalten.
Jeder Test sollte für sich alleine laufen können – unabhängig von anderen Tests. Dies ist entscheidend, um gültige Testergebnisse zu gewährleisten . Einzelheiten dazu finden Sie im Abschnitt „Daten, die wir früher geteilt haben“ in einem meiner letzten Artikel. Sehen Sie sich zunächst das vollständige Beispiel auf GitHub an, wenn Sie den gesamten Test sehen möchten.
Fazit
End-to-End-Tests sind aus meiner Sicht ein wesentlicher Bestandteil von CI, um die Qualität der Bewerbungen auf hohem Niveau zu halten und gleichzeitig die Arbeit der Tester zu entlasten. Cypress ist mein bevorzugtes Tool, um End-to-End-Tests schnell, stabil und effizient zu debuggen und sie parallel zu beliebigen Pull-Requests als Teil von CI auszuführen. Die Lernkurve ist sanft, wenn Sie bereits mit JavaScript vertraut sind.
Ich hoffe, ich konnte Sie ein wenig anleiten und Ihnen einen Ausgangspunkt zum Schreiben von Cypress-Tests sowie einige praktische Tipps für den Einstieg geben. Natürlich sind alle Codebeispiele im GitHub-Repository verfügbar, also schauen Sie sich gerne um.
Dies ist natürlich nur ein Ausgangspunkt; Es gibt noch viel mehr Dinge zu lernen und zu besprechen in Bezug auf Cypress-Tests – ich gebe Ihnen einige Vorschläge, was Sie als Nächstes lernen sollten. In diesem Sinne viel Spaß beim Testen!
Ressourcen
- Original Smashing-Exemplar, Ramona Schwering
GitHub-Repository für das Beispiel in diesem Artikel. - Cypress-Dokumentation
- „Rezepte“, Zypresse
Eine Auswahl an Beispielen, Rezepten und Kursen. - „Programmieren mit JavaScript lernen: Cypress“ (Lektion), CodeLikeThis
- Best Practices zum Schreiben von End-to-End-Tests“, Shopware Docs