{"id":14906,"date":"2019-01-30T14:30:38","date_gmt":"2019-01-30T13:30:38","guid":{"rendered":"https:\/\/www.inovex.de\/blog\/?p=14906"},"modified":"2022-11-22T10:12:49","modified_gmt":"2022-11-22T09:12:49","slug":"screenshot-testing-mit-backstopjs-sotw","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/","title":{"rendered":"Screenshot Testing mit BackstopJS [State of the Web]"},"content":{"rendered":"<p>BackstopJS ist ein <a href=\"https:\/\/github.com\/garris\/BackstopJS\" target=\"_blank\" rel=\"noopener\">Testing Tool<\/a>, das bei Web-Frontends zum Einsatz kommt. Mit seiner Hilfe k\u00f6nnen schnell und einfach visuelle Regressionstests zu einer bereits bestehenden Anwendung hinzugef\u00fcgt werden. Dazu werden als erstes Screenshots aller Seiten erstellt, gegen die vor einem neuen Release gepr\u00fcft wird. Erkennt Backstop dabei ungewollte Unterschiede, schlagen die entsprechenden Tests fehl. Dadurch lassen sich solche \u00c4nderungen schnell erkennen und wiederkehrende Fehler in bereits getesteten Bereichen einer Webanwendung oder Website vermeiden. Gerade beim Refactoring k\u00f6nnen sich Fehler einschleichen, die so direkt Auswirkungen auf die visuellen Verhaltensweisen haben.<!--more--><\/p>\n<p>In der aktuellsten Version werden die Browser Safari, Chrome, Edge und Firefox unterst\u00fctzt.<\/p>\n<p>BackstopJS wurde unter anderem von Garris Shipon gegr\u00fcndet, der als Software-Entwickler bei LinkedIxn arbeitet. Das Projekt steht als OpenSource Projekt unter der MIT License zur Verf\u00fcgung.<\/p>\n<p>In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_83 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\"><\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Backstop-Projekt-aufsetzen\" >Backstop-Projekt aufsetzen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Workflows\" >Workflows<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Die-Konfigurationsdatei\" >Die Konfigurationsdatei<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#BackstopJS-mit-Docker\" >BackstopJS mit Docker<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Test-Workflow\" >Test Workflow<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Eigene-Interaktionen\" >Eigene Interaktionen<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Einbindung-in-Gitlab-CI\" >Einbindung in Gitlab CI<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#v370\" >v3.7.0<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Zusammenfassung-und-Fazit\" >Zusammenfassung und Fazit<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#Alle-Artikel-dieser-Serie\" >Alle Artikel dieser Serie<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Backstop-Projekt-aufsetzen\"><\/span>Backstop-Projekt aufsetzen<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Das Aufsetzen eines Projekts ist einfach. Eine globale Installation ist \u00fcber den Befehl<\/p>\n<pre class=\"lang:default decode:true\">$ npm install -g backstopjs<\/pre>\n<p>auszuf\u00fchren, damit man die Befehle \u00fcber die Kommandozeile ausf\u00fchren kann. Alternativ ist eine lokale Einbindung in ein Projekt m\u00f6glich. Hier ein einfaches Beispiel:<\/p>\n<pre class=\"lang:js decode:true \">const backstop = require('backstopjs');<\/pre>\n<p>Damit kann man \u00fcber die API in JavaScript noch mehr M\u00f6glichkeiten nutzen. Mit dem Befehl<\/p>\n<pre class=\"lang:default decode:true\"> $ backstop init<\/pre>\n<p>initialisiert man das Projekt. Achtung: Dieser Workflow \u00fcberschreibt alle existierenden Files! Im Projekt findet man nun eine Datei mit dem Namen <span class=\"lang:sh decode:true crayon-inline\">backstop.json<\/span>.<\/p>\n<p>Bevor die Installation von Backstop final ausgef\u00fchrt werden kann, ben\u00f6tigt man, falls nicht vorhanden, noch Puppeteer, sonst schl\u00e4gt die Installation gegebenenfalls fehl. Der Befehl<\/p>\n<pre class=\"lang:default decode:true \">$ yarn add puppeteer<\/pre>\n<p>sollte in den meisten F\u00e4llen reichen.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Workflows\"><\/span>Workflows<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Diese vier Workflows sind wichtig: der zum Erstellen von Referenzbildern, der zum ausf\u00fchren der Tests und der Workflow, der die Referenzbilder durch die Testbilder ersetzt. Ein weiterer Workflow ist wichtig, um das Projekt aufzusetzen \u2013 sp\u00e4ter braucht man ihn nicht mehr.<\/p>\n<p>Mit dem Befehl<\/p>\n<pre class=\"lang:default decode:true\">$ backstop reference<\/pre>\n<p>erstellt man Referenzbilder, deren Links man hinterlegen kann. Mehr dazu unten.<\/p>\n<p>Der Befehl<\/p>\n<pre class=\"lang:default decode:true\">$ backstop test<\/pre>\n<p>erstellt Testbilder und vergleicht sie mit den Referenzbildern. Auch hier kann man Links hinterlegen.<\/p>\n<p>Mit dem Befehl<\/p>\n<pre class=\"lang:default decode:true\">$ backstop approve<\/pre>\n<p>kann man die Referenzbilder durch die Testbilder ersetzen.<\/p>\n<p>Mit<\/p>\n<pre class=\"lang:default decode:true\">$ backstop init<\/pre>\n<p>initialisiert man das Projekt wie bereits beschrieben.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Die-Konfigurationsdatei\"><\/span>Die Konfigurationsdatei<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Die <span class=\"lang:sh decode:true crayon-inline \">backstop.json<\/span>\u00a0 ist sehr einfach zu verstehen und besteht wie jede Konfigurationsdatei aus festen Objekten.<\/p>\n<div>\n<pre class=\"lang:js decode:true\" title=\"backstop.json\">{\r\n\r\n  \"id\": \"backstop_default\",\r\n\r\n  \"viewports\": [\r\n\r\n   {\r\n\r\n     \"label\": \"phone\",\r\n\r\n     \"width\": 320,\r\n\r\n     \"height\": 480\r\n\r\n   },\r\n\r\n   {\r\n\r\n    \"label\": \"tablet\",\r\n\r\n    \"width\": 1024,\r\n\r\n    \"height\": 768\r\n\r\n   }\r\n\r\n  ],\r\n\r\n  \"onBeforeScript\": \"puppet\/onBefore.js\",\r\n\r\n  \"onReadyScript\": \"puppet\/onReady.js\",\r\n\r\n  \"scenarios\": [\r\n\r\n   {\r\n\r\n    \"label\": \"BackstopJS Homepage\",\r\n\r\n     \"cookiePath\": \"backstop_data\/engine_scripts\/cookies.json\",\r\n\r\n     \"url\": \"https:\/\/garris.github.io\/BackstopJS\/\",\r\n\r\n     \"referenceUrl\": \"\",\r\n\r\n     \"readyEvent\": \"\",\r\n\r\n     \"readySelector\": \"\",\r\n\r\n     \"delay\": 0,\r\n\r\n     \"hideSelectors\": [],\r\n\r\n     \"removeSelectors\": [],\r\n\r\n     \"hoverSelector\": \"\",\r\n\r\n     \"clickSelector\": \"\",\r\n\r\n     \"postInteractionWait\": 0,\r\n\r\n     \"selectors\": [],\r\n\r\n     \"selectorExpansion\": true,\r\n\r\n     \"expect\": 0,\r\n\r\n     \"misMatchThreshold\" : 0.1,\r\n\r\n     \"requireSameDimensions\": true\r\n\r\n   }\r\n\r\n  ],\r\n\r\n  \"paths\": {\r\n\r\n  \"bitmaps_reference\": \"backstop_data\/bitmaps_reference\",\r\n\r\n  \"bitmaps_test\": \"backstop_data\/bitmaps_test\",\r\n\r\n  \"engine_scripts\": \"backstop_data\/engine_scripts\",\r\n\r\n  \"html_report\": \"backstop_data\/html_report\",\r\n\r\n  \"ci_report\": \"backstop_data\/ci_report\"\r\n\r\n },\r\n\r\n \"report\": [\"browser\"],\r\n\r\n \"engine\": \"puppeteer\",\r\n\r\n \"engineOptions\": {\r\n\r\n \"args\": [\"--no-sandbox\"]\r\n\r\n },\r\n\r\n \"asyncCaptureLimit\": 5,\r\n\r\n \"asyncCompareLimit\": 50,\r\n\r\n \"debug\": false,\r\n\r\n \"debugWindow\": false\r\n\r\n}<\/pre>\n<\/div>\n<p>Mit den <span class=\"lang:sh decode:true crayon-inline \">viewports<\/span>\u00a0 legt man fest, unter welchen Voraussetzungen abh\u00e4ngig von der Gr\u00f6\u00dfe man die Anwendung testen m\u00f6chte. F\u00fcr jede abgelegte Gr\u00f6\u00dfe wird jeder Test einmal durchlaufen. Zum Start sind zwei Gr\u00f6\u00dfen gegeben, die modifiziert werden d\u00fcrfen. Unter den <span class=\"lang:sh decode:true crayon-inline \">scenarios<\/span>\u00a0 kann man die Tests ablegen. Hierbei ist die URL wichtig, in der die zu testende Anwendung abgelegt ist. Die Referenz-URL ist optional; ist diese leer, wird die URL als Referenz-URL genutzt. Alles andere wird in den folgenden Kapiteln n\u00e4her mit Beispielen erkl\u00e4rt.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"BackstopJS-mit-Docker\"><\/span>BackstopJS mit Docker<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<pre class=\"lang:default decode:true\">$ backstop test --docker<\/pre>\n<p>Mit der Flag <span class=\"lang:sh decode:true crayon-inline \">&#8211;docker<\/span>\u00a0 delegiert BackstopJS den Befehl an Docker, um ihn dort auszuf\u00fchren; das funktioniert bei allen Workflows.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Test-Workflow\"><\/span>Test Workflow<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Mit<\/p>\n<pre class=\"lang:default decode:true\">$ backstop test<\/pre>\n<p>erzeugt man die Testbilder und startet automatisch die Auswertung. Das folgende Bild zeigt eine Browserauswertung, die sich automatisch \u00f6ffnet. Eine Einbindung in die CI wird unten erkl\u00e4rt.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"fusion-preview-image\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-17.05.13.png\" sizes=\"auto, \" srcset=\"\" alt=\"\" width=\"2549\" height=\"1204\" \/><\/p>\n<p><i> Beispiel einer Browserauswertung mit erfolgreichen Tests <\/i><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"fusion-preview-image\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-17.07.58.png\" sizes=\"auto, \" srcset=\"\" alt=\"\" width=\"2558\" height=\"1206\" \/><\/p>\n<p><i> Beispiel einer Browserauswertung eines fehlerhaften Tests <\/i><\/p>\n<p>Seit der Version 3.7.0 ist eine Filterung m\u00f6glich, f\u00fcr welche die obere Leiste zust\u00e4ndig ist. Hier kann man sich beispielsweise nur erfolgreiche oder fehlgeschlagene Tests anzeigen lassen. Der Freitextfilter ist f\u00fcr individuell benannte Tests gedacht.<\/p>\n<p>BackstopJS punktet hier mit seiner visuellen Darstellung der unterschiedlichen Pixel. In der Browser-Auswertung werden sie pink markiert.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Eigene-Interaktionen\"><\/span>Eigene Interaktionen<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>W\u00e4hlt man die Option, alles \u00fcber die Backstop-Datei zu testen, kann man ganz einfach Selektoren hinzuf\u00fcgen. Hierbei gibt das Standardszenario schon Selektoren vor, etwa Click oder Hover. So kann man in die Komponente auch hineingreifen, um zus\u00e4tzliche Tests zu schreiben. Die Selektoren zu definieren ist sehr einfach gehalten: Selektoren nach IDs fangen mit einem Hashtag an und Klassen mit einem Punkt. Einem Button mit der ID <span class=\"lang:js decode:true crayon-inline \">id=\u2019myButton\u2019<\/span>\u00a0 bekommt den Click-Selektor <span class=\"lang:js decode:true crayon-inline\">#myButton<\/span>.<\/p>\n<p>Optional kann man auch eine Verz\u00f6gerung einbauen, die der Applikation Zeit gibt, wenn man etwas zur Laufzeit testen m\u00f6chte. Die <span class=\"lang:sh decode:true crayon-inline \">misMatchTreshold<\/span>\u00a0 gibt den Teil in Prozent an, wie viele Pixel maximal verschieden sein d\u00fcrfen, damit der Test nicht fehlschl\u00e4gt.<\/p>\n<p><span class=\"lang:sh decode:true crayon-inline \">ClickSelector<\/span>\u00a0 im Beispiel:<\/p>\n<div>\n<pre class=\"lang:xhtml decode:true\">&lt;!DOCTYPE HTML&gt;\r\n\r\n&lt;html&gt;\r\n\r\n  &lt;body&gt;\r\n\r\n    &lt;h1 id=\"myHeader\"&gt;Hello World!&lt;\/h1&gt;\r\n\r\n    button id=\"myButton\" onClick=\"displayResult()\"&gt;Change text&lt;\/button&gt;\r\n\r\n    &lt;script&gt;\r\n\r\n     function displayResult() {\r\n\r\n       document.getElementById(\"myHeader\").innerHTML = \"Have a nice day!\";\r\n\r\n     }\r\n\r\n    &lt;\/script&gt;\r\n\r\n  &lt;\/body&gt;\r\n\r\n&lt;\/html&gt;<\/pre>\n<\/div>\n<p><i> Beispiel-HTML <\/i><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"fusion-preview-image\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-17.13.17.png\" sizes=\"auto, \" srcset=\"\" alt=\"\" width=\"480\" height=\"173\" \/><\/p>\n<p><i> HTML-Beispiel im Browser <\/i><\/p>\n<p>Nimmt man ein einfaches Beispiel, das einen Button anzeigt, der durch Klicken den Text \u00e4ndert, kann man \u00fcber Backstop mit einem <span class=\"lang:sh decode:true crayon-inline\">ClickSelector<\/span>\u00a0 den Button ausw\u00e4hlen. Das Resultat sollte der Erwartung entsprechen.<\/p>\n<div>\n<pre class=\"lang:js decode:true\">\"clickSelector\": \"#myButton\",<\/pre>\n<\/div>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"fusion-preview-image\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-17.16.04.png\" sizes=\"auto, \" srcset=\"\" alt=\"\" width=\"2537\" height=\"1207\" \/><\/p>\n<p><i> Ergebnis der Auswertung <\/i><\/p>\n<h2><span class=\"ez-toc-section\" id=\"Einbindung-in-Gitlab-CI\"><\/span>Einbindung in Gitlab CI<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Die gr\u00f6\u00dften Vorteile bietet BackstopJS\u00a0 bei der Ausf\u00fchrung automatisierter Tests. Dazu sind ein paar \u00c4nderungen notwendig, die jedoch keine weiteren Probleme bereiten:<\/p>\n<div>\n<pre class=\"lang:yaml decode:true\" title=\".gitlab-ci.yml\">stages:\r\n\r\n  - test\r\n\r\ncache:\r\n\r\n  paths:\r\n\r\n    - node_modules\r\n\r\nbefore_script:\r\n\r\n  - npm install\r\n\r\ntest:\r\n\r\n  image:\r\n\r\n    name: backstopjs\/backstopjs:v3.7.0\r\n\r\n    entrypoint: [\"\"]\r\n\r\n  stage: test\r\n\r\n  tags:\r\n\r\n    - shared\r\n\r\n  script:\r\n\r\n    - backstop reference\r\n\r\n    - backstop test<\/pre>\n<\/div>\n<p><em>Eine sehr triviale Implementierung einer yml-Datei<\/em><\/p>\n<p>Das Image muss implementiert sein, idealerweise mit der aktuellsten Version. Der <span class=\"lang:sh decode:true crayon-inline \">entrypoint<\/span>\u00a0 ist wichtig, wenn man einen einfachen Shared Runner nutzt.<\/p>\n<p>Die Skripte erkl\u00e4ren sich von selbst. Man sollte jedoch beachten, ob man immer Referenzbilder erstellt.<\/p>\n<pre class=\"lang:js decode:true\">\"report\": [\"CI\"]<\/pre>\n<p>In der Datei <span class=\"lang:sh decode:true crayon-inline \">backstop.json<\/span>\u00a0 muss der <span class=\"lang:sh decode:true crayon-inline \">report<\/span>\u00a0 auf <span class=\"lang:sh decode:true crayon-inline \">CI<\/span>\u00a0 gesetzt sein. Das Report-Format ist bereits im Projekt hinterlegt.<\/p>\n<pre class=\"lang:js decode:true\">\"ci\": {\r\n\r\n\u00a0\u00a0\"format\" : \u00a0\"junit\" ,\r\n\r\n\u00a0\u00a0\"testReportFileName\": \"myproject-xunit\",\r\n\r\n\u00a0\u00a0\"testSuiteName\" : \u00a0\"backstopJS\"\r\n\r\n},<\/pre>\n<p>Optional kann man das <span class=\"lang:sh decode:true crayon-inline\">ci<\/span>-Objekt hinzuf\u00fcgen. Dieses \u00fcberschreibt die vorgegebene Konfiguration von Backstop. Hier kann man Pfad, Format und den Namen \u00fcberschreiben, wie und wo die Ergebnisse der CI-Auswertung hinterlegt werden.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"v370\"><\/span><b>v3.7.0<\/b><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>BackstopJS ist bereits bei Version 3.7.0 angekommen und bietet inzwischen erweiterte Features, wie zum Beispiel das anpassbare User Interface des In-Browser Reporters. Ebenfalls mit der dritten Version von BackstopJS wurde der Diff-Inspector eingef\u00fchrt,\u00a0inklusive Scenario-Filter. Seit der dritten Version werden die Tests mit Chrome Headless, Phantom und Slimer gerendert. User k\u00f6nnen Interaktionen mit Puppeteer, ChromeJS und CasperJS simulieren. BackstopJS ist als Standalone Package App global oder lokal runnable und unterst\u00fctzt integriertes Docker Rendering, um plattform\u00fcbergreifendes schlechtes Rendering zu vermeiden.<\/p>\n<p>Die Anbindung zur CI ist in 3.7.0 verbessert und vereinfacht worden, au\u00dferdem sind die Workflows einfacher gehalten, es existieren nur noch drei notwendige Befehle, um alles zu steuern.<\/p>\n<p><a href=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-14914\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58.png\" alt=\"\" width=\"1000\" height=\"792\" srcset=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58.png 1000w, https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58-300x238.png 300w, https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58-768x608.png 768w, https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58-400x317.png 400w, https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/Screen-Shot-2019-01-09-at-16.48.58-360x285.png 360w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><\/p>\n<h2><span class=\"ez-toc-section\" id=\"Zusammenfassung-und-Fazit\"><\/span>Zusammenfassung und Fazit<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>BackstopJS bietet eine einfache Methode f\u00fcr visuelle Regressionstest. Von der Einrichtung bis zur Nutzung und Auswertung wurde die aktuellste Version ma\u00dfgeblich vereinfacht und verringert dadurch auch den Zeitaufwand. Im praktischen Bereich l\u00e4sst sich alles vom User selber einstellen und steuern. F\u00fcr ein kostenlos bereitgestelltes Testframework funktioniert es schnell und zuverl\u00e4ssig.<\/p>\n<p>Vergleichsweise gibt es nur wenige Nachteile: L\u00e4stig wird es nur, wenn\u00a0sich die Tests h\u00e4ufen und man die Konfigurationsdatei manipulieren muss. Hier es sich an, stattdessen die API in JavaScript zu nutzen.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Alle-Artikel-dieser-Serie\"><\/span>Alle Artikel dieser Serie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<ul>\n<li><a href=\"https:\/\/www.inovex.de\/blog\/css-scroll-snap-state-of-the-web\/\">CSS Scroll Snap<\/a><\/li>\n<li><a href=\"https:\/\/www.inovex.de\/blog\/webauthn-authentication\/\">WebAuthn<\/a><\/li>\n<li><a href=\"https:\/\/www.inovex.de\/blog\/native-browser-dialogs-libraries\/\">Native Browser Dialogs<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>BackstopJS ist ein Testing Tool, das bei Web-Frontends zum Einsatz kommt. Mit seiner Hilfe k\u00f6nnen schnell und einfach visuelle Regressionstests zu einer bereits bestehenden Anwendung hinzugef\u00fcgt werden. Dazu werden als erstes Screenshots aller Seiten erstellt, gegen die vor einem neuen Release gepr\u00fcft wird. Erkennt Backstop dabei ungewollte Unterschiede, schlagen die entsprechenden Tests fehl. Dadurch lassen [&hellip;]<\/p>\n","protected":false},"author":101,"featured_media":15289,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[70],"service":[444],"coauthors":[{"id":101,"display_name":"Viet Duc Mai","user_nicename":"vmai"}],"class_list":["post-14906","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-web","service-frontend"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH<\/title>\n<meta name=\"description\" content=\"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2019-01-30T13:30:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-11-22T09:12:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1440\" \/>\n\t<meta property=\"og:image:height\" content=\"810\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Viet Duc Mai\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs-1024x576.png\" \/>\n<meta name=\"twitter:creator\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Viet Duc Mai\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"8\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Viet Duc Mai\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/\"},\"author\":{\"name\":\"Viet Duc Mai\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/1373172afd7dd7011f38b87a43b9d510\"},\"headline\":\"Screenshot Testing mit BackstopJS [State of the Web]\",\"datePublished\":\"2019-01-30T13:30:38+00:00\",\"dateModified\":\"2022-11-22T09:12:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/\"},\"wordCount\":1123,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/backstopjs.png\",\"keywords\":[\"Web\"],\"articleSection\":[\"Applications\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/\",\"name\":\"Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/backstopjs.png\",\"datePublished\":\"2019-01-30T13:30:38+00:00\",\"dateModified\":\"2022-11-22T09:12:49+00:00\",\"description\":\"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/backstopjs.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2019\\\/01\\\/backstopjs.png\",\"width\":1440,\"height\":810,\"caption\":\"A stylized globe with the title of this article on a rose ribbon\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/screenshot-testing-mit-backstopjs-sotw\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Screenshot Testing mit BackstopJS [State of the Web]\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/inovexde\",\"https:\\\/\\\/x.com\\\/inovexgmbh\",\"https:\\\/\\\/www.instagram.com\\\/inovexlife\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/inovex\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/1373172afd7dd7011f38b87a43b9d510\",\"name\":\"Viet Duc Mai\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g56370c1b8f443174b355c924da4bd24d\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g\",\"caption\":\"Viet Duc Mai\"},\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/author\\\/vmai\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH","description":"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/","og_locale":"de_DE","og_type":"article","og_title":"Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH","og_description":"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.","og_url":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2019-01-30T13:30:38+00:00","article_modified_time":"2022-11-22T09:12:49+00:00","og_image":[{"width":1440,"height":810,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png","type":"image\/png"}],"author":"Viet Duc Mai","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs-1024x576.png","twitter_creator":"@inovexgmbh","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Viet Duc Mai","Gesch\u00e4tzte Lesezeit":"8\u00a0Minuten","Written by":"Viet Duc Mai"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/"},"author":{"name":"Viet Duc Mai","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/1373172afd7dd7011f38b87a43b9d510"},"headline":"Screenshot Testing mit BackstopJS [State of the Web]","datePublished":"2019-01-30T13:30:38+00:00","dateModified":"2022-11-22T09:12:49+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/"},"wordCount":1123,"commentCount":3,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png","keywords":["Web"],"articleSection":["Applications"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/","url":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/","name":"Screenshot Testing mit BackstopJS [State of the Web] - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png","datePublished":"2019-01-30T13:30:38+00:00","dateModified":"2022-11-22T09:12:49+00:00","description":"In diesem Blog Post gebe ich einen \u00dcberblick, wie erste Tests f\u00fcr BackstopJS geschrieben werden. Dar\u00fcber hinaus erkl\u00e4re ich, wie man BackstopJS in Docker ausf\u00fchrt, welche Befehle zur Verf\u00fcgung stehen und wie visuelle Tests in einer Gitlab-Pipeline ausgef\u00fchrt werden k\u00f6nnen.","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2019\/01\/backstopjs.png","width":1440,"height":810,"caption":"A stylized globe with the title of this article on a rose ribbon"},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/screenshot-testing-mit-backstopjs-sotw\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"Screenshot Testing mit BackstopJS [State of the Web]"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/1373172afd7dd7011f38b87a43b9d510","name":"Viet Duc Mai","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g56370c1b8f443174b355c924da4bd24d","url":"https:\/\/secure.gravatar.com\/avatar\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8379cdcd6530a325d6548354eac35a1cb5b1f6ca53d233b2ee6fb44df20ba9bf?s=96&d=retro&r=g","caption":"Viet Duc Mai"},"url":"https:\/\/www.inovex.de\/de\/blog\/author\/vmai\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/14906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/101"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=14906"}],"version-history":[{"count":2,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/14906\/revisions"}],"predecessor-version":[{"id":39439,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/14906\/revisions\/39439"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/15289"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=14906"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=14906"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=14906"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=14906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}