Integration von Cypress-Tests mit Docker, Buildkite und CICD #frontend@twiliosendgrid
Veröffentlicht: 2020-12-30Wir haben viele Ende-zu-Ende (E2E) Cypress-Tests geschrieben, um zu überprüfen, ob unsere Webanwendungen immer noch wie erwartet mit dem Backend funktionieren. Nachdem wir diese Browser-Automatisierungstests geschrieben haben, möchten wir, dass diese Cypress-Tests immer ausgeführt oder auf irgendeine Weise wie unsere Komponententests ausgelöst werden, bevor wir Code zusammenführen und in bestimmten Umgebungen bereitstellen. Dies führte dazu, dass wir unsere Cypress-Tests in einem Docker-Container ausführen wollten, um ihn in unseren CI-Anbieter (Continuous Integration) und die Maschinen zu integrieren, die wir in der Cloud verwenden, um diese Container auszuführen.
Wenn es um Bereitstellungsabläufe geht, verwenden wir Buildkite als unseren CI-Anbieter. Auf diese Weise können wir einen Build automatisierter Schritte für unsere Anwendung in einer Buildkite-Pipeline generieren, wenn wir vorhaben, Code übergreifend zu verschieben. Für mehr Kontext ist eine Pipeline ein Ort, der normalerweise mit dem Repository einer Anwendung verbunden ist, wo wir Builds ansehen oder Builds mit bestimmten Schritten zur Ausführung auslösen können, wenn Sie Pull-Anforderungen erstellen, neue Codeänderungen pushen, Code zum Master zusammenführen und in verschiedenen Umgebungen bereitstellen . Wir erstellen mehrere Pipelines für verschiedene Zwecke, z. B. für die Bereitstellung, ausgelöste Cypress-Tests und bestimmte Cypress-Tests, die nach einem Zeitplan ausgeführt werden.
In diesem Blogbeitrag wird davon ausgegangen, dass Sie bereits Cypress-Tests geschrieben und einige Tests ausgeführt haben, aber gerne Ideen hätten, wie Sie diese Tests ständig in Ihren Entwicklungs- und Bereitstellungsabläufen ausführen können. Wenn Sie stattdessen mehr über das Schreiben von Cypress-Tests erfahren möchten, können Sie sich diesen früheren Blog-Beitrag ansehen und diesen dann erneut aufrufen, wenn Sie etwas auszuführen haben.
Wir wollen Sie durch Ideen führen, wie Sie Cypress-Tests in einen Docker-Container mit Ihrem CI-Anbieter integrieren können, indem wir uns ansehen, wie wir es mit Docker Compose und Buildkite in unserer Bereitstellungspipeline gemacht haben. Diese Ideen können in Ihrer Infrastruktur für die Strategien, Befehle und Umgebungsvariablen erweitert werden, die beim Auslösen von Cypress-Tests anzuwenden sind.
Unser Standard-CICD-Flow
In unserem standardmäßigen Entwicklungs- und Bereitstellungsablauf richten wir zwei Pipelines ein:
- Der erste behandelt unsere Bereitstellungsschritte für das Pushen von Code.
- Der zweite löst aus, dass unsere Cypress-Tests parallel laufen und aufgezeichnet werden. Der Erfolg oder Misserfolg davon wirkt sich auf die Bereitstellungspipeline aus.
In unserer Bereitstellungspipeline bauen wir unsere Webanwendungsressourcen aus, führen Komponententests durch und haben Schritte, um ausgewählte Cypress-Tests auszulösen, bevor wir sie in jeder Umgebung bereitstellen. Wir stellen sicher, dass sie bestehen, bevor wir die Fähigkeit zur Bereitstellung per Knopfdruck aufheben. Diese ausgelösten Cypress-Tests in der zweiten Pipeline werden ebenfalls in einem Docker-Container ausgeführt und sind über einen Aufzeichnungsschlüssel mit dem kostenpflichtigen Cypress-Dashboard verbunden , sodass wir auf die Videos, Screenshots und Konsolenausgaben dieser Cypress-Tests zurückblicken können, um Probleme zu beheben.
Unter Verwendung der ausgewählten Eingaben von Buildkite haben wir ein dynamisches System entwickelt, wählen Sie Ihr eigenes Abenteuer aus, damit Benutzer „Ja“ oder „Nein“ auswählen können, um zu entscheiden, welche Cypress-Spezifikationsordner ausgeführt und überprüft werden sollen, während wir mehr Code übertragen. Die Standardantwort wäre „Nein“ für alle Optionen, aber der Wert „Ja“ wäre der Glob-Pfad zum Cypress-Spezifikationsordner.
Manchmal möchten wir nicht alle Cypress-Tests ausführen, wenn sich unsere Codeänderung nicht auf andere Seiten auswirkt. Wir wollen stattdessen nur die Tests auslösen, von denen wir wissen, dass sie betroffen sind. Möglicherweise müssen wir auch eine schnelle Lösung für die Produktion für ein dringendes Fehlerproblem bereitstellen, da wir zuversichtlich genug sind, unsere Cypress-Tests nicht auszuführen, die je nach Anzahl der ausgelösten Tests zwischen 0 und 10 Minuten dauern können. Wir bieten ein Beispiel sowohl visuell als auch in den YML-Schritten für diesen Teil.


Als Nächstes haben wir unser eigenes Bash-Skript namens runCypress.sh , das nach diesem Auswahlschritt ausgeführt wird, um die ausgewählten „Ja“- oder „Nein“-Werte zu analysieren. Wir tun dies, um eine Liste von kommagetrennten Spezifikationspfaden zu erstellen, die ausgeführt und als Option --spec an unseren eventuellen Cypress-Befehl angehängt werden, der in einem Docker-Container in einer ausgelösten Pipeline ausgeführt wird. Wir exportieren Umgebungsvariablen wie die gebildete Liste der Spezifikationen in „CYPRESS_SPECS“ und die aktuelle Testumgebung in „CYPRESS_TEST_ENV“, um sie in der Pipeline zu verwenden, die wir am Ende des Skripts mit buildkite-agent pipeline upload "$DIRNAME"/triggerCypress.yml .
Sie haben vielleicht bemerkt, dass wir auch eine „ASYNC“-Umgebungsvariable exportieren. In Buildkite können Sie wählen, ob ein ausgelöster Build-Schritt in Bezug auf Erfolg oder Misserfolg blockierend oder nicht blockierend sein soll. Wenn wir „ASYNC“ auf „true“ gesetzt haben, werden unsere Hauptschritte der Bereitstellungspipeline weiter ausgeführt und warten nicht, bis die ausgelösten Cypress-Tests in einer anderen Pipeline abgeschlossen sind. Der Erfolg oder Fehler der Pipeline wirkt sich nicht auf den Erfolg oder Fehler der Bereitstellungspipeline aus.
Wenn „ASYNC“ auf „false“ gesetzt ist, werden unsere Hauptschritte der Bereitstellungspipeline blockiert, bis die ausgelösten Cypress-Tests in einer anderen Pipeline abgeschlossen sind. Der Erfolg oder Misserfolg des ausgelösten Builds führt zum Gesamterfolg oder Misserfolg der Bereitstellungspipeline, wo es danach weitergeht.
Wenn sich unser Code noch in einem Feature-Branch mit einem offenen Pull-Request befindet, möchten wir weitere Änderungen pushen, einige Cypress-Tests auslösen und sehen, wie sich die Dinge verhalten. Wir möchten jedoch nicht immer die Ausführung der restlichen Schritte der Bereitstellungspipeline blockieren, wenn die ausgelösten Tests fehlschlagen, da auf dem Weg möglicherweise weitere Änderungen vorgenommen werden. In diesem Szenario setzen wir „ASYNC“ auf „false“, um nicht zu blockieren, wenn Cypress-Tests fehlschlagen. Für den Fall, dass wir unsere Pull-Anforderung bereits in den Master zusammengeführt und für das Staging bereitgestellt haben, aber Cypress-Tests auslösen möchten, bevor wir sie für die Produktion bereitstellen, setzen wir „ASYNC“ auf „true“, da wir möchten, dass die Cypress-Tests immer bestanden werden, bevor sie in die Produktion gehen .
Kehren wir zu runCypress.sh zurück, erinnern wir uns, dass das Skript die Ausführung der zweiten Pipeline auslöst, indem es die Datei triggerCypress.yml mit zugewiesenen Umgebungsvariablenwerten aufruft. Die Datei triggerCypress.yml sieht etwa so aus. Sie werden feststellen, dass der „Trigger“-Schritt und die Interpolation von Werten in die Build-Meldungen für das Debugging und dynamische Schrittnamen hilfreich sind.
Unabhängig davon, ob wir die Ausführung der Cypress-Tests von unserer Bereitstellungspipeline zu einer separaten Trigger-Pipeline auslösen oder die Cypress-Tests nach einem Zeitplan in einer dedizierten Pipeline ausführen, befolgen und verwenden wir dieselben Schritte wieder, während wir nur die Werte der Umgebungsvariablen ändern.
Diese Schritte beinhalten:
- Erstellen des Docker-Images mit einem neuesten Tag und einem eindeutigen Versionstag
- Hochladen des Docker-Images in unsere private Registry
- Ziehen Sie dasselbe Image herunter, um unsere Cypress-Tests basierend auf unseren Umgebungsvariablenwerten in einem Docker-Container auszuführen
Diese Schritte sind in einer pipeline.cypress.yml -Datei wie folgt beschrieben:

Wenn wir die Ausführung von Cypress-Tests auslösen, wird ein separater Build in der Cypress-Trigger-Pipeline gestartet. Basierend auf dem Erfolg oder Misserfolg des Builds wird der Cypress-Testlauf entweder blockieren oder es uns ermöglichen, für die Produktion bereitzustellen, wenn wir von der Staging- zur Produktion für Master-Branch-Builds wechseln.

Wenn Sie auf den Schritt „Triggered cypress/integration/…“ klicken, gelangen Sie zum Build der getriggerten Pipeline mit einer Ansicht wie dieser, um zu sehen, wie die Tests verlaufen sind.

Wenn Sie neugierig sind, wie der Docker-Teil vollständig verbunden ist, verwenden unsere Dockerfile.cypress und docker-compose.cypress.yml diese aus unseren Pipelines exportierten Umgebungsvariablen, um dann den richtigen Cypress-Befehl aus der package.json unserer Anwendung zu verwenden, der nach rechts zeigt Testumgebung und Ausführen der ausgewählten Spezifikationsdateien. Die folgenden Ausschnitte zeigen unseren allgemeinen Ansatz, den Sie erweitern und verbessern können, um flexibler zu sein.
Außerhalb der Tests, die während unserer üblichen Integrations- und Bereitstellungszyklen ausgeführt werden, haben wir dedizierte Buildkite-Pipelines erstellt. Diese Pipelines werden nach einem Zeitplan für wichtige Tests in unserer Staging-Umgebung ausgeführt, um sicherzustellen, dass unsere Front-End- und Back-End-Dienste ordnungsgemäß funktionieren. Wir haben ähnliche Pipeline-Schritte wiederverwendet, bestimmte Umgebungsvariablenwerte in den Einstellungen der Buildkite-Pipeline angepasst und einen Cron-Zeitplan eingerichtet, der zu einem geplanten Zeitpunkt ausgeführt wird. Dies hilft uns, viele Fehler und Probleme mit der Staging-Umgebung zu erkennen, während wir weiterhin überwachen, wie gut unsere Tests abschneiden und ob irgendetwas nachgelagertes oder von unseren eigenen Code-Pushs dazu geführt haben könnte, dass Tests fehlgeschlagen sind.

Parallelisierung
Wir nutzen auch das Parallelisierungs-Flag, um die Anzahl der AWS-Maschinen zu nutzen, die wir aus unserer von unserem Ops-Team eingerichteten Warteschlange von Build-Agenten hochfahren können. Mit diesem Parallelisierungs-Flag ruft Cypress automatisch eine bestimmte Anzahl von Maschinen auf, basierend auf der Anzahl, die wir in der „Parallelism“-Eigenschaft von Buildkite festgelegt haben.
Wir konnten über 200 Tests in etwa 5 Minuten für eines unserer Anwendungs-Repos ausführen.
Anschließend werden alle Cypress-Tests so verteilt, dass sie parallel auf diesen Computern ausgeführt werden, während die Aufzeichnung aller Tests für einen bestimmten Build-Lauf beibehalten wird. Dies hat unsere Testlaufzeiten dramatisch erhöht!
Hier sind einige Tipps zur Parallelisierung Ihrer Cypress-Tests:
- Befolgen Sie die Vorschläge im Dashboard-Dienst für die optimale Anzahl von Computern und lassen Sie die Anzahl der Computer in einer Umgebungsvariablen festlegen, um Flexibilität in Ihren Pipelines zu gewährleisten.
- Durch die Aufteilung in kleinere Testdateien, insbesondere durch die Aufteilung länger laufender Tests in Blöcke, können wir besser über Maschinen hinweg parallelisieren.
- Stellen Sie sicher, dass Ihre Cypress-Tests isoliert sind und sich nicht gegenseitig beeinflussen oder voneinander abhängen. Verwenden Sie beim Umgang mit Flows im Zusammenhang mit Aktualisierungen, Erstellungen oder Löschungen separate Benutzer und Datenressourcen, um zu vermeiden, dass sich Tests gegenseitig stören und in Race-Bedingungen geraten. Ihre Testdateien können in beliebiger Reihenfolge ausgeführt werden, stellen Sie also sicher, dass dies kein Problem darstellt, wenn Sie alle Ihre Tests ausführen.
- Denken Sie für Buildkite daran, den Wert der
--ci-build-idID-Umgebungsvariable zusätzlich zur Optionparallelan die Option --ci-build-id zu übergeben, damit es weiß, welcher eindeutige Build-Lauf zuzuordnen ist, wenn Tests über mehrere Computer hinweg parallelisiert werden.
Zur Überprüfung:
Um Ihre Cypress-Tests mit Ihrem CI-Anbieter wie Buildkite zu verbinden, müssen Sie:
- Erstellen Sie ein Docker-Image mit Ihrem Anwendungscode, indem Sie das erforderliche Cypress-Basisimage und die Abhängigkeiten verwenden, die erforderlich sind, um die Tests in einer Node-Umgebung für bestimmte Browser auszuführen.
- Pushen Sie Ihr Docker-Image mit bestimmten Tags in eine Registrierung
- Ziehen Sie dasselbe Bild in einem späteren Schritt nach unten
- Führen Sie Ihre Cypress-Tests im Headless-Modus und mit Aufnahmetasten aus, wenn Sie den Cypress Dashboard-Dienst verwenden.
- Legen Sie verschiedene Werte für Umgebungsvariablen fest und fügen Sie sie in die Befehle ein, die Sie für Cypress ausführen, um ausgewählte Cypress-Tests für eine bestimmte Testumgebung in diesen Docker-Containern auszulösen.
Diese allgemeinen Schritte können wiederverwendet und auf Cypress-Tests angewendet werden, die nach einem Zeitplan und anderen Anwendungsfällen ausgeführt werden, z. B. das Auslösen von Tests, die zusätzlich zu Ihren Bereitstellungspipelines für ausgewählte Browser ausgeführt werden. Der Schlüssel liegt darin, die Fähigkeiten Ihres CI-Anbieters zu nutzen und Ihre Befehle so einzurichten, dass sie basierend auf Umgebungsvariablenwerten flexibel und konfigurierbar sind.
Richten Sie Ihre Befehle so ein, dass sie basierend auf Umgebungsvariablenwerten flexibel und konfigurierbar sind.
Sobald Sie Ihre Tests in Docker bei Ihrem CI-Anbieter ausgeführt haben (und wenn Sie für den Dashboard-Service bezahlen), können Sie die Vorteile der Parallelisierung Ihrer Tests auf mehreren Computern nutzen. Möglicherweise müssen Sie vorhandene Tests und Ressourcen so ändern, dass sie nicht voneinander abhängig sind, um zu vermeiden, dass sich Tests gegenseitig stören.
Wir haben auch Ideen besprochen, die Sie selbst ausprobieren können, z. B. das Erstellen einer Testsuite zur Validierung Ihrer Backend-API oder das Auslösen von Tests, die mit einem von Ihnen ausgewählten Browser ausgeführt werden. Es gibt auch weitere Möglichkeiten, Continuous Integration hier in den Cypress-Dokumenten einzurichten .
Darüber hinaus ist es wichtig, diese Cypress-Tests während des Bereitstellungsflusses oder in geplanten Intervallen auszuführen, um sicherzustellen, dass Ihre Entwicklungsumgebungen jederzeit wie erwartet funktionieren. Unzählige Male haben unsere Cypress-Tests Probleme im Zusammenhang mit nachgelagerten Backend-Diensten entdeckt, die ausgefallen waren oder sich auf irgendeine Weise geändert haben, was sich in Frontend-Anwendungsfehlern manifestierte. Sie haben uns insbesondere vor unerwarteten Fehlern in unseren Webseiten bewahrt, nachdem wir neue React-Code-Änderungen veröffentlicht hatten.
Die Aufrechterhaltung bestandener Tests und die sorgfältige Überwachung fehlgeschlagener Testläufe in unseren Testumgebungen führen zu weniger Support-Tickets und zufriedeneren Kunden in der Produktion. Eine gesunde und stabile Suite von Cypress-Tests am Laufen zu halten, wenn Sie neue Codeänderungen pushen, gibt Ihnen mehr Vertrauen, dass die Dinge gut funktionieren, und wir empfehlen Ihnen und Ihren Teams, dasselbe mit Ihren Cypress-Tests zu tun.
Weitere Ressourcen zu Cypress-Tests finden Sie in den folgenden Artikeln:
- Was beim Schreiben von E2E-Tests zu beachten ist
- 1.000-Fuß-Übersicht über das Schreiben von Cypress-Tests
- Schreiben Sie alle Dinge in Ihre Cypress-Tests
- Umgang mit E-Mail-Flows in Cypress-Tests
- Ideen zum Konfigurieren, Organisieren und Konsolidieren Ihrer Cypress-Tests
