
Eine wesentliche Herausforderung in Entwicklungsprojekten, die CI/CD implementieren, ist der damit verbundene kontinuierliche Testaufwand. Die ständig zunehmende Komplexität der Codebasis und der entwickelten Anwendung erhöht sowohl den Aufwand als auch die Notwendigkeit von Regressionstests kontinuierlich. Dennoch ermöglicht uns die wiederkehrende Natur dieser Tests, sie automatisiert nach dem Deploy-Prozess durchzuführen. In diesem Beispiel-Setup wird gezeigt, wie Cypress mithilfe von Docker das Kontaktformular unserer Website scalewerk.io testet.
In diesem Setup werden nach dem Klick auf den Kontakt-Button die Felder unseres Kontaktformulars auf ihre Existenz und Pflichtfeldeigenschaften überprüft. Zudem wird am Ende geprüft, ob die E-Mail-Adresse, die wir als Umgebungsvariable übergeben, vom Formular akzeptiert wird. Das Setup läuft pseudo-headless und verzichtet auf die kostenpflichtigen Cypress-Cloud-Services, um eine einfache Integration in eine Jenkins-Pipeline zu ermöglichen. Wie genau die Ergebnisse dann ohne visuelle Oberfläche aussehen und ausgewertet werden können, wird nach dem Setup noch erwähnt. Für das Setup sind aber erstmal drei Komponenten wichtig:
Projekt-Struktur
Die hier gezeigte Projektstruktur ist der Standard, mit dem Cypress die nötigen Dateien im Container findet. Dabei ist lediglich der Ablageort der "cypress.config.js" nicht änderbar; der Pfad zu den Spec- und Support-Dateien kann in eben dieser noch geändert werden (wie durch Kommentare im Code angedeutet).
project
|-- cypress
| |-- e2e
| | |-- contact_form.cy.js
| |-- support
| | |--e2e.js
cypress.config.js
Projekt-Dateien
cypress.config.js
Die Config-Datei, welche die Ausführungs-Einstellungen der E2E-Tests definiert.
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: "https://scalewerk.io/",
supportFile: "cypress/support/e2e.js", //Wird die oben gezeigte Struktur einghalten ist diese Zeile nicht nötig, sonst kann hier der Ablageort des Support-Files angegeben werden
specPattern: "cypress/e2e/**/*.cy.js", //Wird die oben gezeigte Struktur einghalten ist diese Zeile nicht nötig, sonst kann hier das Pattern der Spec-Files angegeben werden
},
screenshotOnRunFailure: false, //Es ist hilfreich diese Option zu aktivieren, wenn auch Headless an den Tests entwickelt wird aber in der Pipeline ist es praktischer diese nicht unnötig aufzublähen mit Screenshots
})
Um die Tests zu debuggen, kann "screenshotOnRunFailure" auf true gesetzt werden, damit bei lokalen Ausführungen eine visuelle Darstellung abgelegt wird, sollten die Tests fehlschlagen. Dies kann sowohl zum Debuggen der Tests als auch der getesteten Anwendung verwendet werden.
e2e.js
Die Support-Datei, welche zum Beispiel custom Commands enthält. Commands in dieser Datei abzulegen hat Vorteile bzgl. der Loadorder, sowie von Clean Code in den eigentlichen Test-Dateien.
Cypress.Commands.add('checkInput', (locator, text, isRequired) => {
if (isRequired) {
cy.get(locator).should('have.attr', 'required')
}
cy.get(locator).type(text)
})
contact_form.cy.js
Die eigentliche Test- bzw. Spec-Datei, welche den genauen Testlauf definiert.
const email = Cypress.env('email') || 'john.doe@scalewerk.io'
describe('contact_form.cy', () => {
it('Gets, types and asserts', () => {
cy.visit('')
cy.contains('Kontakt').click()
cy.url().should('include', '#contact-form')
cy.checkInput('[name="firstname"]', 'John', true)
cy.checkInput('[name="lastname"]', 'Doe', true)
cy.checkInput('[name="company"]', 'Company', true)
cy.checkInput('[name="email"]', email, true)
cy.checkInput('[name="phone"]', '+12 3456 7890', false)
cy.checkInput('[name="message"]', 'Hello World!', false)
cy.contains('Bitte geben Sie eine andere E-Mail-Adresse ein. Dieses Formular akzeptiert keine Adressen von').should('not.exist')
})
})
Docker Command
Der folgende Befehl ist das Herzstück, das in der Pipeline ausgeführt werden muss, z.B. in einem Groovy-Script für Jenkins. Mit ihm wird der Docker-Container gestartet, wobei standardmäßig der "cypress"-Entrypoint verwendet wird, um unsere Tests durchzuführen. So wie hier beschrieben sollte der Befehl im "project"-Verzeichnis ausgeführt werden.
docker run --rm -v $PWD:/e2e -w /e2e cypress/included --env email='john.doe@gmail.com'
- rm - Entfernt den Container nach Beendigung der Tests
- v $PWD:/e2e - Mappt das aktuelle Verzeichnis auf "e2e" im Container (Falls Änderungen an der Projektstruktur vorgenommen werden, sollten auch entsprechende Anpassungen hier erfolgen.)
- w /e2e - Wechselt in das Arbeitsverzeichnis "e2e"
- cypress/included - Cypress-Docker-Image; das included Image besteht aus Cypress, einigen Browsern und deren Abhängigkeiten
- env email='john.doe@gmail.com' - Übergibt die E-Mail-Adresse "john.doe@gmail.com" als Umgebungsvariable
Ergebnisse
Ohne die visuelle Oberfläche erhalten wir die Ergebnisse in folgender Form in der Konsole. Neben den Ergebnissen der einzelnen Tests erhalten wir noch einige Zusatzinformationen, wie z.B. Laufzeit, verwendete Browser, Node-Version, etc.
Im folgenden Bild ist das Ergebnis einer erfolgreichen Ausführung unseres Beispiels zu sehen. Als E-Mail-Adresse haben wir dazu 'john.doe@scalewerk.io' angegeben.
Hier das Ergebnis nach einer nicht erfolgreichen Ausführung. Die angegebene E-Mail-Adresse wurde dafür durch 'john.doe@mail.com' ersetzt. In Rot angekreidet zeigt uns die Ausgabe, dass die Seite unsere E-Mail-Adresse abgelehnt hat. An sich ist das natürlich ein gewünschtes Verhalten der Website, hier dient es jedoch als Anschauungsbeispiel für einen gescheiterten Test.
Führt man den Testlauf lokal aus, kann man im Nachhinein folgenden Screenshot in seinem Projekt finden. Diesen können wir erhalten, da die Testausführung nur außerhalb von Docker, pseudo-headless läuft. Er ist abgelegt unter:
project
|-- screenshots
| |-- contact_form.cy -- Gets, types and asserts (failed).png
Zwar könnte dieser Screenshot auch in einer Pipeline erstellt werden, jedoch ist er dort schwerer zugänglich. Er sollte nach der Ausführung explizit entfernt werden, um die Nodes sauber zu halten.
Mit all diesen Informationen kann nun also die Fehleranalyse beginnen. Bei unserem Beispiel bleibt es ratsam, den Test um eine Unterscheidung zwischen gültigen und ungültigen E-Mail-Adressen zu erweitern.
Tipp: Um in einem Jenkins-Setup auch eine übersichtliche Farbmarkierung der Ergebnisse zu erhalten, kann der Docker-Command mit dem AnsiColor-Plugin escaped werden.
Mit diesem kurzen Setup können wir das offizielle Cypress-Image beispielsweise in ein Groovy-Script für Jenkins einbinden, um Cypress dort ohne die kostenpflichtige Cloud-Oberfläche auszuführen. Für die Ausführung ist nur ein einziges Kommando notwendig, sowohl auf einer Remote-Node als auch lokal. Für die Entwicklung der Anwendung sowie der Tests kann mit nur einer schnellen Anpassung auch visuelles Feedback eingeholt werden, sollten die Tests fehlschlagen. Durch die Ausführung in Docker wird zudem sichergestellt, dass die Tests lokal und remote in einer vergleichbaren Umgebung durchgeführt werden.