Neuigkeiten von trion.
Immer gut informiert.

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.

Installation von Cypress
$ 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.

Aufruf von Cypress aus NPM-Script
"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.

Beispiel Cypress Test-Case in 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.

Cypress Container in Docker für React Anwendung
$ 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"

> basic-demo@0.1.0 start /app
> react-scripts start

> basic-demo@0.1.0 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.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels