React Anwendungen mit Cypress in Docker testen
Container, allen voran Docker, sind in aktuellen Infrastrukturen ein fester Bestandteil. Waren früher gerade Buildserver und CI-Umgebungen schwer zu warten, da diverse Werkzeuge oftmals jeweils in mehreren Versionen gepflegt werden müssen, können Build-Container alle benötigten Werkzeuge direkt mitbringen.
Moderne Buildserver wie DroneCI oder GitLab CI unterstützen daher nativ die Verwendung von Werkzeugcontainern in Form von Docker Images. Für Angular gibt es beispielsweise mit https://hub.docker.com/r/trion/ng-cli-karma ein Image, in dem Angular CLI und Webbrowser bereitgestellt werden.
In diesem Beitrag werfen wir einen Blick auf Cypress, dass sich sehr gut zur Umsetzung von Integrations- und Ende-zu-Ende Tests eignet.
Cypress kann prinzipiell mit beliebigen Technologien kombiniert werden, beispielsweise serverseitig gerenderten Anwendungen, oder mit Angular, React oder Vue im Browser umgesetzten Anwendungen.
Wir verwenden in diesem Beispiel React, jedoch sollte die Übertragung auf andere Frameworks dem Entwickler sehr einfach von der Hand gehen.
Einrichtung Cypress
Als Ausgangsbasis dient eine mit create-react-app
erzeugte React-Anwendung.
Cypress wird als Abhängigkeit mit npm
installiert.
$ npm install cypress --save-dev
Nun kann Cypress durch $(npm bin)/cypress open
gestartet werden.+
Praktischerweise erzeugt Cypress die notwendigen Ordnerstrukturen und Konfigurationsdateien automatisch, wenn das Projekt noch nicht für Cypress konfiguriert ist.
Nach dem ersten Aufruf findet sich ein neuer Ordner cypress
im Projektverzeichnis, in dem sich fixtures
, integration
, plugins
und support
als Unterordner finden, sowie eine cypress.json
im Hauptverzeichnis.
Im Ordner cypress/integration
werden die eigentlichen Tests abgelegt, Beispiele werden unter examples
durch Cypress mitgebracht.
Um den Start der Tests zu vereinfachen, kann ein entsprechendes Kommando in der package.json
im scripts
Abschnitt ergänzt werden.
Da Cypress mehrere Browser unterstützt, können natürlich auch mehrere Kommandos angelegt werden.
Wir wollen den Firefox Browser im Headless-Modus verwenden und legen dazu ein e2e
Kommando an.
"scripts": {
"e2e": "cypress run --browser firefox --headless"
}
Der Aufruf kann dann auf der Kommandozeile durch npm run e2e
erfolgen.
Der nächste Schritt ist die Definition von Testfällen:
Wir erzeugen unter cypress/integration/
eine Datei sample.js
, die unsere Testbeschreibung beeinhaltet.
cypress/integration/sample.js
/// <reference types="cypress" />
context('Navigation', () => {
beforeEach(() => {
cy.visit('http://localhost:3000')
})
it('cy.reload() - reload the page', () => {
cy.reload()
// reload the page without using the cache
cy.reload(true)
})
it('contains increase', () => {
cy.contains('+')
});
it('contains decrease', () => {
cy.contains('-')
});
it('increases', () => {
cy.contains('+').click();
cy.contains('+').click();
cy.get('.output').should("contain.text", 2)
});
})
Nun geht es um die eigentliche Ausführung der Tests in einem Docker Container.
Ausführung Cypress in Docker
Cypress stellt bereits fertige Docker-Images auf Docker-Hub zur Verfügung, sodass der Aufwand entfällt, eigene Images zu erstellen.
Unter cypress/browsers
finden sich verschiedene Tags, die die Zusammensetzung des Images beschreiben:
Aus den Komponenten Node-Version, Chrome-Version und Firefox-Version entsteht dann beispielsweise cypress/browsers:node12.13.0-chrome80-ff74
.
Wichtig bei der Ausführung ist, dass der Cypress Docker-Container Zugriff auf das Projektverzeichnis bekommt.
Das wird über einen Docker Volume-Mount realisiert (-v
).
Damit erzeugte Dateien auch dem aktuellen Nutzer gehören, vor allem unter Linux ein Thema, wird der Container mit der Nutzer-Id des aktuellen Nutzers ausgeführt (-u
).
Um wiederkehrende Downloads aus dem Internet zu reduzieren, wird ein zusätzlicher Volume-Mount für den Cypress-Cache Ordner verwendet und über die Umgebungsvariable CYPRESS_CACHE_FOLDER
der Speicherort in Cypress konfiguriert (-e
).
Das Arbeitsverzeichnis wird noch auf den Mountpoint des Projektverzeichnis im Container konfiguriert, damit die Tests und Ausgabeordner gefunden werden (-w
).
Am Schluß ist noch darauf zu achten, dass die zu testende Applikation bereitsteht.
In diesem Fall wird die React Anwendung auf einen Node Server durch npm run start
zur Verfügung gestellt.
Parallel dazu werden die Cypress-Tests durch npm run e2e
gestartet.
$ docker run --rm -u $(id -u) \
-v $(pwd):/app -v $HOME/.cache/Cypress:/cache \
-w /app -e CYPRESS_CACHE_FOLDER=/cache \
cypress/browsers:node12.13.0-chrome80-ff74 \
sh -c "npm run start & npm run e2e"
> [email protected] start /app
> react-scripts start
> [email protected] e2e /app
> cypress run --browser firefox --headless
ℹ 「wds」: Project is running at http://172.17.0.5/
ℹ 「wds」: webpack output is served from
ℹ 「wds」: Content not from webpack is served from /app/public
ℹ 「wds」: 404s will fallback to /
Starting the development server...
Files successfully emitted, waiting for typecheck results...
Compiled successfully!
You can now view basic-demo in the browser.
Local: http://localhost:3000
On Your Network: http://172.17.0.5:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
================================================================
(Run Starting)
┌─────────────────────────────────────────────────────────────┐
│ Cypress: 4.1.0 │
│ Browser: Firefox 74 (headless) │
│ Specs: 1 found (sample.js) │
└─────────────────────────────────────────────────────────────┘
─────────────────────────────────────────────────────────────────
Running: sample.js (1 of 1)
Navigation
✓ cy.reload() - reload the page (977ms)
✓ contains increase (459ms)
✓ contains decrease (341ms)
✓ increases (580ms)
4 passing (4s)
(Results)
┌─────────────────────────────────────────────────────────────┐
│ Tests: 4 │
│ Passing: 4 │
│ Failing: 0 │
│ Pending: 0 │
│ Skipped: 0 │
│ Screenshots: 0 │
│ Video: true │
│ Duration: 3 seconds │
│ Spec Ran: sample.js │
└─────────────────────────────────────────────────────────────┘
(Video)
- Started processing: Compressing to 32 CRF
- Finished processing: /app/cypress/videos/sample.js.mp4 (0 seconds)
=================================================================
(Run Finished)
Spec Tests Passing Failing Pending Skipped
┌───────────────────────────────────────────────────────────────────────┐
│ ✔ sample.js 00:03 4 4 - - - │
└───────────────────────────────────────────────────────────────────────┘
✔ All specs passed! 00:03 4 4 - - -
Nachdem Cypress die Tests ausgeführt hat, wird ein Report ausgegeben, der mitteilt, welche Tests ausgeführt wurden, wie lange die Tests gelaufen sind, und welche erfolgreich bzw. nicht erfolgreich waren. Cypress erzeugt Screenshots im Fehlerfall und auch anhand eines aufgezeichneten Videos können Fehlersituation nachvollzogen werden.
Diese werden dank des Docker Volume Mounts im regulären Projektverzeichnis abgelegt. Auf einem CI-Server erfolgt die Ausführung der Cypress Tests in Docker dann analog.
Zu den Themen React, *Angular und Vue bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an.
Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.