23.2.2020
Um den ganzen Artikel etwas plastischer zu gestalten werde ich das Thema anhand der HTML Templates von Foundation for Sites durchgehen. Dabei handelt es sich um exemplarisch gestaltete Webseitenlayouts für verschiedene Anwendungsfälle.
Foundation HTML Templates
Zusätzlich füge ich ein Stylesheet custom.css
hinzu, um Veränderungen herbeizuführen.
Dadurch ergibt sich folgende Baumstruktur:
.
|__news-magazine.html
|__ecommerce.html
|__blog.html
|__blog-simple.html
|__portfolio.html
|__custom.css
|__product-page.html
|__real-estate.html
|__marketing-site.html
|__index.html
|__agency.html
Zusätzlich gibt es bei Abschnitten in denen Anpassungen vorgenommen werden Verweise auf entsprechende Commits in einem Backstop Demo Projekt.
Am einfachsten startet man mit Backstop indem man es global über npm installiert. Dazu führt man den folgenden Befehl aus:
npm install backstopjs --global
Anschließend kann man die Installation mit backstop -v
überprüfen. Als Ergebnis sollte die aktuelle Version von Backstop ausgegeben werden. Zum jetzigen Zeitpunkt ist die aktuellste Version BackstopJS v4.4.2
.
Wenn die Installation erfolgreich war kann Backstop in jedem Verzeichnis ausgeführt werden.
Nachdem Backstop global installiert ist kann mit dem folgenden Befehl ein HTTP Server mit dem Port 3000 in diesem Verzeichnis gestartet werden.
backstop remote
Es können auch Dateipfade aus dem Betriebssystem verwendet werden. In der Praxis hat man für gewöhnlich einen Browsersync oder eine Live Seite. Backstop kann alle Pfade aufrufen, die auch der Browser aufrufen kann.
Zunächst muss eine Grundeinrichtung für Backstop erfolgen. Dazu führt man in dem Projektverzeichnis den folgenden Befehl aus. Damit wird Backstop in einer Standardkonfiguration eingerichtet.
backstop init
Anschließend sieht das Projektverzeichnis wie folgt aus:
.
|__news-magazine.html
|__backstop_data
| |__engine_scripts
| | |__chromy
| | | |__onReady.js
| | | |__onBefore.js
| | | |__loadCookies.js
| | | |__clickAndHoverHelper.js
| | |__cookies.json
| | |__imageStub.jpg
| | |__puppet
| | | |__overrideCSS.js
| | | |__onReady.js
| | | |__onBefore.js
| | | |__loadCookies.js
| | | |__interceptImages.js
| | | |__ignoreCSP.js
| | | |__clickAndHoverHelper.js
|__ecommerce.html
|__blog.html
|__blog-simple.html
|__portfolio.html
|__custom.css
|__product-page.html
|__real-estate.html
|__marketing-site.html
|__backstop.json
|__index.html
|__agency.html
Es wurden ein backstop_data
Verzeichnis und backstop.json
Dokument angelegt. Damit kann nun Backstop verwendet werden.
Übrigens: Backstop Testfälle müssen nicht zwangsläufig im Projektverzeichnis definiert werden, jedoch macht eine Koppelung von dem Projekt und den Testdokumenten in den meisten Fällen Sinn.
Im letzten Schritt ist die Datei backstop.json
angelegt. Hier sieht man die komplette Konfiguration:
{
"id": "backstop_default",
"viewports": [
{
"label": "phone",
"width": 320,
"height": 480
},
{
"label": "tablet",
"width": 1024,
"height": 768
}
],
"onBeforeScript": "puppet/onBefore.js",
"onReadyScript": "puppet/onReady.js",
"scenarios": [
{
"label": "BackstopJS Homepage",
"cookiePath": "backstop_data/engine_scripts/cookies.json",
"url": "https://garris.github.io/BackstopJS/",
"referenceUrl": "",
"readyEvent": "",
"readySelector": "",
"delay": 0,
"hideSelectors": [],
"removeSelectors": [],
"hoverSelector": "",
"clickSelector": "",
"postInteractionWait": 0,
"selectors": [],
"selectorExpansion": true,
"expect": 0,
"misMatchThreshold" : 0.1,
"requireSameDimensions": true
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
"engine_scripts": "backstop_data/enanzen Artikel etwas plastischer zu gestalten werde ich das Thema anhand der HTML Templates von Foundation for Sites durchgehen. Dabei handelt es sich um exemplarisch gestaltete Webseitenlayouts für verschiedene Anwendungsfälle.gine_scripts",
"html_report": "backstop_data/html_report",
"ci_report": "backstop_data/ci_report"
},
"report": ["browser"],
"engine": "puppeteer",
"engineOptions": {
"args": ["--no-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false
}
id
Gibt den Präfix für die erstellten Screenshots an
viewports
Legt fest, welche Auflösungen getestet werden sollen. Dabei müssen folgende Punkte beachtet werden:
label
wird dem Screenshot Namen angehängt, um diesen zuordnen zu können.width
gibt die Breite des ViewPorts an, bei der die Screenshots aufgenommen werden.height
gibt die Höhe des ViewPorts an, bei der die Screenshots aufgenommen werden.
body
handelt. Hat man einen mehr spezifischen Selektor, so wird die Höhe des Elementes verwendet, anstatt das Element bei der Höhe des ViewPorts abzuschneiden.scenarios
Hierbei handelt es sich um ein Array von Objekten. Ein Szenario beschreibt eine Seite, ihre Elemente und Aktionen. Dabei gibt es verschiedene Einstellungen für die einzelnen Szenarios. Diese sind gut dokumentiert. Hier gehe ich auf die Wichtigsten ein:
label
gibt die Bezeichnung für das Szenario an. Die Bezeichnung wird an den Screenshot Namen angehängt.url
gibt die URL an, unter welcher man die Screenshots machen möchteselectors
legt die Selektoren fest. Dabei schreibt man die Selektoren, wie man sie in CSS und JS auswählen würde.engine
Legt fest, welche Browser Engine verwendet werden soll. Der Standardwert ist puppeteer
, er kann aber auch gegen chromy
ausgetauscht werden. In der Praxis kann man bei der Einrichtung die beiden Engines austauschen und schauen, ob eine davon bessere Ergebnisse (schnellere Tests, bessere Darstellung, kosntant bleibende Screenshots, …) liefert und dann bei dieser bleiben.
engineOptions
Hier können diverse Einstellungen für die jeweiligen Engines eingetragen werden. Ein Anwendungsfall wäre z.B. die Print Version einer Seite testen zu wollen.
asyncCaptureLimit
Legt fest, wie viele Browser gleichzeitig aufgerufen werden können. Bei diesem Wert gibt es keine feste Zahl die ich empfehlen könnte. Meine Handlungsempfehlung wäre es, den schwächsten Computer zu finden, der die Backstop Tests durchführen muss, niedrig anzufangen und dann die Zahl so lange hochzudrehen bis dieser überfordert ist.
asyncCompareLimit
Legt fest, wie viele Screenshots gleichzeitig verglichen werden können. Hier gibt es die gleiche Handlungsempfehlung wie bei asyncCaptureLimit
.
Die restlichen Optionen sollten für gewöhnlich nicht angepasst werden. Bei Bedarf findet man aber auf der github Seite von Backstop zu fast allen Optionen eine Erklärung.
Nachdem wir nun wissen was die einzelnen Optionen bewirken, können wir damit anfangen diese für unseren Anwendungsfall anzupassen.
Als erstes sollte man die id
zu etwas Aussagekräftigem wie in diesem Fall zu Foundation Template
ändern.
Anschließend könnte man einen weiteren viewport
wie z.B. desktop
mit einer Breite von 1440px hinzufügen.
Zusätzlich habe ich die Option enginge
auf chromy
umgestellt, da diese für mich zuverlässigere Testergebnisse liefert.
Nun kommt der spannende Teil. Die Szenarios sind nämlich alles das, was wir testen wollen.
Dazu sammelt man zunächst alle Seiten die man testen möchte.
Fangen wir mit index.html
an.
Seitenstruktur
Auf dieser Seite sollen die wichtigsten Elemente getestet werden. Das sind die Navigation, das Callout, die einzelnen Artikel, die Seitenleiste und die Pagination.
Dafür würde das Szenario wie folgt aussehen:
{
"label": "index",
"url": "http://localhost:3000/index.html",
"selectors": [
".top-bar",
".callout",
".blog-post",
".sticky-container",
".pagination"
],
"selectorExpansion": true
}
Hierbei ist zu beachten, dass die Option selectorExpansion
auf true
gesetzt ist. Das ist wichtig, weil der Standardwert false
dafür sorgt, dass lediglich das erste Vorkommen des Selektors für die Tests verwendet wird.
Anschließend geht man alle Seiten nacheinander durch und erstellt alle relevanten Testfälle, sodass die Datei backstop.json
am Ende ungefähr wie folgt aussieht. Die nachfolgenden Szenarios wurden soweit vereinfacht, dass die Selektoren lediglich als .row
dargestellt worden sind. Für echte Testfälle sollte man aber ein wenig mehr Arbeit investieren, um möglichst kleinteilige Testfälle zu erhalten.
{
"id": "Foundation Template",
"viewports": [
{
"label": "phone",
"width": 320,
"height": 480
},
{
"label": "tablet",
"width": 1024,
"height": 768
},
{
"label": "desktop",
"width": 1440,
"height": 900
}
],
"onBeforeScript": "puppet/onBefore.js",
"onReadyScript": "puppet/onReady.js",
"scenarios": [
{
"label": "index",
"url": "http://localhost:3000/index.html",
"selectors": [
".top-bar",
".callout",
".blog-post",
".sticky-container",
".pagination"
],
"selectorExpansion": true
},
{
"label": "agency",
"url": "http://localhost:3000/agency.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "news-magazine",
"url": "http://localhost:3000/news-magazine.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "ecommerce",
"url": "http://localhost:3000/ecommerce.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "blog",
"url": "http://localhost:3000/blog.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "blog-simple",
"url": "http://localhost:3000/blog-simple.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "portfolio",
"url": "http://localhost:3000/portfolio.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "product-page",
"url": "http://localhost:3000/product-page.html",
"selectors": [
".row:not(.medium-up-3)"
],
"selectorExpansion": true
},
{
"label": "real-estate",
"url": "http://localhost:3000/real-estate.html",
"selectors": [
".row"
],
"selectorExpansion": true
},
{
"label": "marketing-site",
"url": "http://localhost:3000/marketing-site.html",
"selectors": [
".row"
],
"selectorExpansion": true
}
],
"paths": {
"bitmaps_reference": "backstop_data/bitmaps_reference",
"bitmaps_test": "backstop_data/bitmaps_test",
"engine_scripts": "backstop_data/engine_scripts",
"html_report": "backstop_data/html_report",
"ci_report": "backstop_data/ci_report"
},
"report": ["browser"],
"engine": "chromy",
"engineOptions": {
"args": ["--no-sandbox"]
},
"asyncCaptureLimit": 5,
"asyncCompareLimit": 50,
"debug": false,
"debugWindow": false
}
GitHub Commit für die Initialeinrichtung
Nachdem die Testfälle stehen kann man nun mit dem Arbeiten beginnen.
Im ersten Schritt führt man den Befehl backstop test
aus.
BackstopJS erster Durchlauf
In der Konsole bekommt man eine Fehlermeldung und der Standardbrowser öffnet sich. Sowohl auf der Konsole als auch im Browser sieht man, dass alle Tests fehlgeschlagen sind. Das liegt daran, dass noch keine Referenzbilder vorliegen. Man sollte sich alle erstellten Screenshots anschauen und wenn das dem entspricht was man erwartet führt man backstop approve
aus. um die Screenshots als Referenzbilder festzulegen. Führt man nun erneut backstop test
aus, sollten keine Fehler mehr auftreten.
Somit haben wir eine Ausgangsbasis mit der wir arbeiten können.
Für das Beispiel soll die custom.css
mit folgendem CSS ergänzt werden:
a {
padding-left: 4px;
}
Das führt dazu, dass alle Links um 4px nach rechts rutschen. Nun führt man den Befehl backstop test
aus.
Es kann sein, dass die Anpassungen im Code keine Auswirkungen auf das Aussehen der Seite haben und es keine fehlgeschlagenen Tests gibt. Der Normalfall jedoch ist, dass Tests fehlschlagen. Dann geht man in die Übersicht von Backstop und schaut sich an, ob die Änderungen fehlerhaft sind. Wenn diese genauso gewollt sind, führt man backstop approve
aus und akzeptiert die Testscreenshots. Ansonsten wird der Quellcode angepasst.
In dem Beispiel schlagen einige Testfälle fehl. Das liegt daran, dass die Linkverschiebung erkannt worden ist. Diese Veränderung war genau so zu erwarten und der Testfall wird mit backstop approve
akzeptiert.
Man arbeitet oftmals mit mehreren Personen und der Unterstützung von Versionierungssoftware (von nun an wird Git als Beispiel verwendet) an Projekten.
Dann muss als erstes die Entscheidung getroffen werden, ob die Referenzbilder mit versioniert werden sollen oder nicht.
Beide Vorgehen haben Vor- und Nachteile.
Nachdem man diese Entscheidung getroffen hat, kann man den in backstop.json
angegebenen paths.bitmaps_reference
Pfad ignorieren, falls man die Referenzen nicht versionieren möchte.
Falls man die Referenzen nicht versioniert, kann man mit dem Befehl backstop reference
die Screenshots in einem Schritt testen und akzeptieren.
So ergeben sich folgende zwei Vorgehensweisen:
git pull
backstop test
backstop approve
git pull
backstop reference
backstop test
backstop approve
Wenn man diesem Beitrag gefolgt ist, so ist man jetzt bereit um mit Backstop zu arbeiten. Für große Projekte kann die Einrichtung von Backstop noch weiter optimiert werden. Diesem widmen wir uns in dem nächsten Teil der Backstop Serie.