Difference Testing mit recheck-web: Von der Installation bis zur Stabilisierung
“Assertion Testing” ist der klassische, aus JUnit stammende Ansatz, bei dem manuell erstellte Assertion-Statements bei der Testausführung als Testorakel (zur Unterscheidung zwischen pass/fail) dienen. “Difference Testing” ist im Gegensatz dazu ein Golden-Master-basierter Testansatz, der die Unterschiede zwischen verschiedenen Ausführungen verdeutlicht, beispielsweise zwischen unterschiedlichen Versionen.
Assertion Testing ist ein Blacklist-Ansatz, es werden also nur Unterschiede erkannt, die zuvor per Assertion explizit definiert wurden. Difference Testing ist hingegen ein Whitelist-Ansatz. Hier werden zunächst alle Unterschiede (auch unerwartete) erkannt, irrelevante lassen sich dann ignorieren.
Eine Schritt-für-Schritt-Anleitung
- zum Installieren und Einrichten von recheck-web mit dem Build-Tool Maven und recheck.cli
- zum Erstellen eines ersten ausführbaren Difference-Testing-basierten Java-Testfalls
- sowie zum Stabilisieren der Testausführung, indem irrelevante Änderungen ignoriert werden.
Darüber hinaus zeigt es einige typische Anwendungsszenarien in der Entwicklung und Testwartung.
Voraussetzung für den Einsatz von recheck-web ist, dass Java und Maven auf dem System installiert sind. Entwickler können das überprüfen, indem sie ein Terminal beziehungsweise CMD öffnen und die folgenden Befehle ausführen:
mvn –version
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>recheck-web-tutorial</artifactId>
<version>0.1-SNAPSHOT</version>
<properties><maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>de.retest</groupId>
<artifactId>recheck-web</artifactId>
<version>1.0.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Zur Installation des Chrome-Drivers entpackt man das Archiv auf die Festplatte. Entwickler müssen hier beachten, dass sie zusätzlich eine passende Version von Chrome auf ihrem System benötigen. Danach sollte alles vorbereitet sein.
Der erste recheck-web-Testfall
Ein einfacher Test mit Selenium und recheck-web könnte in Java ungefähr so aussehen:
import org.junit.*;
import org.openqa.selenium.*;
import de.retest.recheck.*;
public class MyFirstTest {
private WebDriver driver;
private Recheck re;
@Before
public void setup() {
? re = new RecheckImpl();
? System.setProperty(“webdriver.chrome.driver”, “C:\\pathto\\chromedriver.exe”);
driver = new ChromeDriver();
}
@Test
public void google() throws Exception {
re.startTest();
driver.get(“http://google.com”);
re.check(driver, “open”);
re.capTest();
}
@After
public void tearDown() {
driver.quit();
re.cap();
}
}
In einem typischen umfangreicheren Test rufen Entwickler check mehrmals auf, jedes Mal mit einer anderen eindeutigen Kennung. Da Unterschiede nicht so ungewöhnlich sind, ist es nicht gewünscht, dass der Test bei einem kleinen Unterschied sofort fehlschlägt. Deshalb werden bei den Aufrufen der Prüfmethode alle Abweichungen gesammelt. Um den Test im Fall von Differenzen fehlschlagen zu lassen, rufen Entwickler am Ende des Tests die Methode capTest auf. Sollten sie den Aufruf vergessen, findet sich eine entsprechende Meldung in der Logausgabe. Nach Abschluss des Tests beendet die @After-Methode Chrome durch den Aufruf von driver.quit() und veranlasst via cap das Erstellen einer zusammenfassenden Berichtsdatei inklusive aller Änderungen.
Lokale Ausführung eines ersten Testfalls
Nun sollten Entwickler den Test mit mvn test ausführen können. Wenn alles korrekt eingerichtet wurde, sieht man beim ersten Ausführen des Tests die folgende Fehlermeldung:
No recheck file found. First time test was run? Created recheck file now, don’t forget to commit…
at de.retest.recheck.RecheckImpl.capTest(RecheckImpl.java:137)
? at com.mycompany.MyFirstTest.google(MyFirstTest.java:30)
at … some more stack trace …
Der Test muss also beim ersten Ausführen fehlschlagen. Aber recheck erzeugt genau dann den Golden Master und legt ihn unter src\test\resources\retest\recheck\com.mycompany.MyFirstTest\google.open.recheck ab. Im Ordner befindet sich nun eine XML-Datei, die alle Attribute jedes HTML-DOM-Elements nach dem Rendern der Website enthält, sofern es sich nicht um die Standardwerte handelt, zusammen mit einem Screenshot der Website. Wenn Entwickler nun ihren Test erneut ausführen, werden alle Elemente und Attribute des Golden Master mit den aktuellen HTML-DOM-Elementen verglichen. Jede nicht ignorierte Differenz eines Elements führt zum Fehlschlagen des Tests. Führen Entwickler das erneut aus (wiederum mit mvn test), wird eine Ausgabe (ohne ignorierte Elemente) ähnlich der folgenden erzeugt:
A detailed report will be created at ‘target\test-classes\retest\recheck\com.mycompany.MyFirstTest.report’. You can review the details by using our GUI from https://retest.de/review/.
The following differences have been found in ‘com.mycompany.MyFirstTest'(with 1 check(s)):
Test ‘google’ has 37 differences in 1 states:
open resulted in:
A [About Google] at ‘HTML[1]/BODY[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/A[1]’:
? ping: expected=”some gibberish”
? … many more differences …
recheck funktioniert ähnlich: Entwickler können alle volatilen und nicht relevanten Unterschiede einfach ignorieren. Das Werkzeug hat einen Ordner mit dem Namen .retest im Projektverzeichnis erstellt. Dort findet man ein Beispiel für die Datei recheck.ignore. Um diese flüchtigen Elemente zu ignorieren und den gegebenen Test grün werden zu lassen, müssen Anwender nur diese Klartextdatei bearbeiten. Das Einfügen des folgenden Inhalts in die Datei sollte den Test erfolgreich durchlaufen lassen:
attribute=jsdata
attribute=data-.*
attribute=class
attribute=outline
attribute=transform
Je nachdem, was Entwickler in der Ignore-Datei angeben, lassen sich verschiedene Testszenarien realisieren. Der allgemeine Mechanismus von recheck ermöglicht es, Funktionstests, Cross-Browser- und Cross-Device-Tests sowie visuelle Regressionstests zu erstellen und durchzuführen. Für die letzteren Testzwecke sollten Entwickler vorsichtig sein mit dem, was sie ignorieren. Für reine Funktionstests können sie meist problem- und risikolos viele CSS-Attribute vernachlässigen.
Pflegen des Golden Master
Wenn Entwickler einen automatisierten Test erstellen, möchten sie, dass er grün ist, wenn sich nichts ändert. Der eigentliche Vorteil eines automatisierten Tests besteht jedoch darin, die Auswirkungen von Änderungen aufzuzeigen, insbesondere unbeabsichtigte Nebenwirkungen. Während der normalen Softwareentwicklung entstehen jedoch regelmäßig viele Änderungen. Mit einem Versionskontrollsystem kann man sie einfach akzeptieren, das heißt commiten. Mit recheck benötigt man die gleichen Funktionen, um den Golden Master zu pflegen.
Dafür müssen Entwickler zum Beispiel die recheck CLI installieren. Dafür entpackt sie einfach das Archiv zum Beispiel in C:\Program Files\recheck.cli-1.0.0. Dann müssen sie noch die Datei recheck.cmd oder recheck (je nach Betriebssystem) zu ihrem Pfad hinzufügen. Wie das zu bewerkstelligen ist, hängt stark vom Betriebssystem und dessen Version ab. Für Windows 10 funktioniert es beispielsweise so: Zuerst die “Einstellungen” öffnen. Dann im Suchfeld “Umgebung” eingeben und “Bearbeiten der Umgebungsvariablen” wählen. Danach klickt man auf die Registerkarte Erweitert | Umgebungsvariablen | Pfad | Bearbeiten | Neu. Schließlich fügt man den Pfad zum Ordner recheck/bin hinzu.
Um Änderungen für die Prüfung und Pflege zu generieren, öffnen Entwickler einen Browser und gehen zu scratchpad.io. Wer die Seite öffnet, wird an eine eindeutige URL weitergeleitet (z. B. http://scratchpad.io/recheck-45678). Jetzt bearbeiten sie den Test aus dem vorherigen Abschnitt und ersetzen den Methodennamen google durch scratchpad und die URL durch die gerade neu erstellte, eindeutige URL von Scratchpad. Der Methodenrumpf sollte dann so ähnlich aussehen:
public void scratchpad() throws Exception {
re.startTest();
driver.get(“http://scratchpad.io/recheck-45678”);
re.check(driver, “open”);
re.capTest();
}
Usage: recheck [–help] [–version] [COMMAND]
Command-line interface for recheck.
–help Display this help message.
–version Display version info.
Commands:
version Display version info.
diff Display given differences.
commit Accept given differences.
ignore Ignore given differences.
completion Generate and display auto completion script.
help Displays help information about the specified command
Test ‘scratchpad’ has 7 differences in 1 states:
open resulted in:
textnode [scratchpad.io] at ‘HTML[1]/BODY[1]/DIV[3]/DIV[2]/DIV[1]/DIV[3]/DIV[23]/textnode[2]’:
text: expected=”scratchpad.io”, actual=”recheck”
DIV at ‘HTML[1]/BODY[1]/DIV[3]/DIV[2]/DIV[1]/DIV[5]/DIV[1]’:
left: expected=”210.125px”, actual=”173.75px”
right: expected=”252.813px”, actual=”289.188px”
style: expected=”left: 210.125px; top: 286px; width: 6.0625px; height: 13px;”, actual=”left: 173.75px; top: 286px; width: 6.0625px; height: 13px;”
TEXTAREA at ‘HTML[1]/BODY[1]/DIV[3]/TEXTAREA[1]’:
left: expected=”255.25px”, actual=”218.875px”
right: expected=”148.297px”, actual=”184.672px”
style: expected=”top: 285px; height: 13px; width: 6.0625px; left: 255.25px;”, actual=”top: 285px; height: 13px; width: 6.0625px; left: 218.875px;”
at de.retest.recheck.RecheckImpl.capTest(RecheckImpl.java:137)
Angenommen, dies ist eine beabsichtigte Änderung und man will den Golden Master aktualisieren. Dazu können Entwickler nun ein CMD im Projektordner öffnen und den folgenden Befehl ausführen:
Updated SUT state file C:\Users\retest\Desktop\recheck-web-tutorial\src\test\resources\retest\recheck\com.mycompany.MyFirstTest\scratchpad.open.recheck
Um weitere Funktionen der recheck CLI zu zeigen, passt man den Inhalt der Scratchpad-Seite noch einmal an. Dazu öffnen Entwickler den Browser und ändern die Begrüßungsnachricht auf recheck-web. Das Ausführen des Tests sollte den Unterschied erneut aufzeigen und eine Berichtsdatei erstellen. Entwickler können nun die recheck CLI nutzen, um den Inhalt der Datei anzuzeigen, indem sie folgenden Befehl ausführen:
Reading JS ignore rules file from C:\Users\retest\Desktop\recheck-web-tutorial\.retest\recheck.ignore.js.
Specified JS ignore file has no ‘shouldIgnoreAttributeDifference’ function.
Specified JS ignore file has no ‘shouldIgnoreElement’ function.
Test ‘scratchpad’ has 5 differences in 1 states:
open resulted in:
textnode [recheck] at ‘HTML[1]/BODY[1]/DIV[3]/DIV[2]/DIV[1]/DIV[3]/DIV[23]/textnode[2]’:
text: expected=”recheck”, actual=”scratchpad.io”
DIV at ‘HTML[1]/BODY[1]/DIV[3]/DIV[2]/DIV[1]/DIV[5]/DIV[1]’:
left: expected=”173.75px”, actual=”210.125px”
right: expected=”289.188px”, actual=”252.813px”
TEXTAREA at ‘HTML[1]/BODY[1]/DIV[3]/TEXTAREA[1]’:
left: expected=”218.875px”, actual=”255.25px”
right: expected=”184.672px”, actual=”148.297px”
Fazit
Das Tutorial hat verdeutlicht, wie man recheck-web und CLI einrichtet und einen auf Difference Testing basierenden automatisierten Selenium-Test erstellt und pflegt. Der Testansatz hat viele Vorteile gegenüber assertionsbasierten Tests. Er ist einfacher einzurichten – keine Identifizierung einzelner Elemente zum Beispiel über XPath oder ID mehr. Er ist einfacher zu warten – statt neue Werte manuell zu kopieren und einzufügen, um den Erwartungswert zu aktualisieren, genehmigen Entwickler die Änderungen mit einem einzigen Befehl. Aber der größte Vorteil ist, dass die Tests viel vollständiger sind als ihre Assertions-basierten Gegenstücke: Sie können gar keine Assertion für eine unerwartete Änderung anlegen. Mit Difference Testing dagegen bleibt keine Veränderung unbemerkt.
Ein semantischer Vergleich ist auch im Vergleich zu den vielen pixelbasierten Tools von Vorteil, die einen Golden-Master-Testansatz implementieren. Heutige Websites sind sehr dynamisch. Mit dem transition-Attribut von CSS möchten Entwickler mehr als nur das reine Aussehen visuell überprüfen, sie möchten auch das dynamische Ladeverhalten untersuchen. Das Überprüfen der CSS-Attribute weist sie direkt auf Änderungen hierbei hin, die sie dann manuell überprüfen können. Statt ganze Bildbereiche grob zu ignorieren, kann man spezifisch angeben, was ignoriert werden soll. Man kann zum Beispiel Schriftart und -größe ignorieren, aber nicht den Text. Man kann sogar ein regelbasiertes Ignorieren von Änderungen implementieren (bspw. Änderungen mit einer Differenz von weniger als 5 Pixel).
Aber dieses regelbasierte Ignorieren wird Teil eines folgenden und tiefer gehenden Tutorials über die erweiterten Funktionen von recheck sein. Es zeigt dann, wie man Tests fast “unzerbrechlich” machen kann, indem man im Golden Master nachschaut, wie der alte Zustand vor einer “breaking change” aussah. Und es wird gezeigt, wie man bestehende herkömmliche Tests migriert und recheck mit anderen Tools wie CI/CD oder Drittanbieter-Testframeworks wie Cucumber integriert.