Die Kunst des Layout-Testens mit Galen Framework

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Bei der Gestaltung einer grafischen Benutzeroberfläche bleibt immer eine offene Frage: Wie automatisieren wir das Testen dafür? Und wie stellen wir sicher, dass das Website-Layout reaktionsschnell bleibt und auf allen Arten von Geräten mit unterschiedlichen Auflösungen korrekt angezeigt wird? Hinzu kommen die Komplikationen, die sich aus dynamischen Inhalten, Anforderungen an Internationalisierung und Lokalisierung ergeben, und es wird zu einer echten Herausforderung. In diesem Artikel werde ich Sie durch eine interessante neue Technik zum Testen von Layouts führen. Unter Verwendung des Galen Frameworks stelle ich ein detailliertes Tutorial zum Schreiben aussagekräftiger generalisierter Layouttests zur Verfügung, die in jedem Browser und auf jedem Gerät ausgeführt und gleichzeitig als Single Source of Truth in Ihrer Designdokumentation verwendet werden können.

Bei der Gestaltung einer grafischen Benutzeroberfläche bleibt immer eine offene Frage: Wie automatisieren wir das Testen dafür? Und wie stellen wir sicher, dass das Website-Layout reaktionsschnell bleibt und auf allen Arten von Geräten mit unterschiedlichen Auflösungen korrekt angezeigt wird? Hinzu kommen die Komplikationen, die sich aus dynamischen Inhalten, Anforderungen an Internationalisierung und Lokalisierung ergeben, und es wird zu einer echten Herausforderung.

In diesem Artikel werde ich Sie durch eine interessante neue Technik zum Testen von Layouts führen. Unter Verwendung des Galen Frameworks biete ich ein detailliertes Tutorial zum Schreiben aussagekräftiger generalisierter Layouttests, die in jedem Browser und auf jedem Gerät ausgeführt und gleichzeitig als Single Source of Truth in Ihrer Designdokumentation verwendet werden können.

Weiterführende Literatur zu SmashingMag:

  • Visuelle testgetriebene Entwicklung für responsives Interface-Design
  • Die Grundlagen der Testautomatisierung für Apps, Spiele und das mobile Web
  • Diverse Frameworks zur Testautomatisierung für React Native Apps

Ich werde auch zeigen, wie ich zu einem optimierten Test für eine Messaging-Seite auf unserer Kleinanzeigen-Website Marktplaats gekommen bin. Wir werden lernen, Galens Syntax mit unserer eigenen Sprache zu erweitern, den Testcode zu verbessern und eine Layout-Testroutine in Kunst zu verwandeln.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Einführung in das Galen-Framework

Galen Framework wurde vor einem Jahr in „Visual Test-Driven Development for Responsive Interface Design“ behandelt. Zu dieser Zeit war seine Syntax begrenzt. Seitdem hat es sich stark verbessert und viele neue Funktionen erhalten, die wir uns hier ansehen werden.

Falls Sie mit Galen Framework nicht vertraut sind, es ist ein Tool für reaktionsfähige und browserübergreifende Layouttests und Funktionstests mit einer eigenen Testsprache namens Galen Specs. Es basiert auf Selenium WebDriver und verfügt außerdem über eine umfangreiche JavaScript-API, mit der Sie direkt mit WebDriver arbeiten können. Da Sie die Kontrolle über WebDriver haben, können Sie Tests in jedem Browser, in der Cloud (SauceLabs, BrowserStack, PerfectoMobile usw.) oder auf echten Mobilgeräten mit Appium durchführen.

Installieren und Ausführen

Das Einrichten von Galen Framework ist einfach. Führen Sie einfach den folgenden Befehl aus, um es über npm zu installieren:

 npm install -g galenframework-cli

Wenn Sie npm nicht verwenden, laden Sie einfach das neueste Galen Framework-Archiv herunter, extrahieren Sie das Paket und folgen Sie den Installationsanweisungen.

Nach der Installation kann Galen Framework auf verschiedene Arten gestartet werden. Beispielsweise können Sie den check verwenden, um einen Schnelltest einer einzelnen Seite zu starten. Für diesen Befehl müssen Sie eine .gspec -Datei mit Ihren Layout-Validierungen bereitstellen, die Sie dann wie folgt aufrufen können:

 galen check loginPage.gspec --url https://example.com --size 1024x768 --include desktop --htmlreport reports

Dieser Befehl startet einen Browser, öffnet die angegebene URL, ändert die Größe des Browserfensters auf 1024 × 768 Pixel und führt alle in der Datei loginPage.gspec deklarierten Validierungen aus. Als Ergebnis erhalten Sie einen detaillierten HTML-Bericht.

Verwalten von Testsuiten

In der realen Welt besteht eine tatsächliche Webanwendung nicht nur aus statischen Seiten. Sehr oft müssen Sie einige Aktionen ausführen, um an die Stelle zu gelangen, die Sie überprüfen möchten. In diesem Fall bietet Galen JavaScript-Testsuiten und die GalenPages-JavaScript-API zur Implementierung des Seitenobjektmodells an. Hier ist ein einfaches Beispiel für einen JavaScript-Galen-Test:

 test("Home page", function() { var driver = createDriver("https://galenframework.com", "1024x768"); checkLayout(driver, "homePage.gspec", ["desktop"]); });

Und hier ist eine Implementierung des Seitenobjektmodells für eine Anmeldeseite aus einem realen Projekt.

 WelcomePage = $page("Welcome page", { loginButton: "#welcome-page .button-login" }); LoginPage = $page("Login page", { username: "input[name='login.username']", password: "input[name='login.password']", loginButton: "button.button-login" loginAs: loggedFunction ("Log in as ${_1.username} with password ${_1.password}", function(user) { this.username.typeText(user.username); this.password.typeText(user.password); this.loginButton.click(); }) }); test("Login page", function() { var driver = createDriver("https://testapp.galenframework.com", "1024x768"); var welcomePage = new WelcomePage(driver).waitForIt(); welcomePage.loginButton.click(); new LoginPage(driver).waitForIt(); checkLayout(driver, "loginPage.gspec", ["desktop"]); });

Für die fortgeschrittene Verwendung empfehle ich Ihnen, sich das Galen Bootstrap-Projekt anzusehen. Es ist eine JavaScript-Erweiterung, die speziell für Galen entwickelt wurde. Es bietet einige zusätzliche Funktionen für UI-Tests und eine einfachere Möglichkeit, Browser zu konfigurieren und komplexe Testsuiten auszuführen.

Einfacher Layout-Test

Lassen Sie mich mit der Einführung eines einfachen Layouttests in Galen Framework beginnen. Dann werde ich zu fortgeschrittenen Anwendungsfällen übergehen und demonstrieren, wie die Syntax von Galen Specs erweitert werden kann. Dazu sehen wir uns eine Kopfzeile mit einem Symbol und einer Beschriftung an:

Symbol und Bildunterschrift
Symbol und Bildunterschrift (Große Version anzeigen)

Im HTML-Code könnte das etwa so aussehen:

 <body> <!-- … --> <div> <img class="header-logo" src="/imgs/header-logo.png"/> <h1>My Blog</h1> </div> <!-- … --> </body>

Die einfachste Form eines Galen-Layouttests würde etwa wie folgt aussehen. Zuerst müssen wir Objekte mit CSS-Selektoren deklarieren.

 @objects header #header icon #header img caption #header h1

Dann deklarieren wir einen Testabschnitt mit einem aussagekräftigen Namen und setzen alle unsere Validierungen darunter.

 = Icon and Caption = icon: left-of caption 10 to 15px width 32px height 32px inside header 10px top caption: aligned horizontally all header inside header

Hier haben wir zwei Header-Elemente getestet: das Symbol und die Beschriftung. Alle Validierungen, die unter den Symbol- und Beschriftungselementen aufgeführt sind, sind eigentlich standardmäßige Galen-Spezifikationen. Diese Spezifikationen sind die grundlegenden Bausteine, mit denen Sie Ihre eigene Layout-Testlösung zusammenstellen können. Jede Spezifikation überprüft eine einzelne Eigenschaft (z. B. Breite, Höhe, Text), die relative Positionierung (z. B. innen, links von, darüber) oder Pixel im Screenshot (z. B. Farbschema, Bild).

Testen mehrerer Elemente mit forEach-Schleifen

Das vorherige Beispiel zeigt ein einfaches Szenario. Mal sehen, wie wir mit einer komplizierteren Situation umgehen können: einem horizontalen Menü. Lassen Sie uns zunächst eine einfache Layout-Testtechnik ausprobieren.

Horizontales Menü
Horizontales Menü (Große Version anzeigen)

Beginnen Sie damit, mehrere Elemente auf der Seite abzugleichen. Mit dem folgenden Code weisen wir Galen an, nach Elementen zu suchen, die mit dem CSS-Selektor #menu ul li .

 @objects menu #menu item-* ul li

Später können wir auf diese Elemente mit Namen wie menu.item-1 und menu.item-2 und mit einer @forEach-Schleife über alle @forEach iterieren.

 = Menu = menu.item-1: inside menu 0px top left bottom @forEach [menu.item-*] as menuItem, next as nextItem ${menuItem}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}

Wie Sie sehen können, ist der Code bereits weniger intuitiv geworden, obwohl die eigentliche Überprüfung nicht so komplex ist. Stellen Sie sich vor, wir hätten in unseren Tests mehr ähnlichen Code. Irgendwann würde es zu einem unhaltbaren Chaos werden. Es sollte eine Möglichkeit geben, es zu verbessern.

Layouttests neu gedacht

Wenn Sie an das vorherige Beispiel denken, könnten wir das Layout in nur ein oder zwei Sätzen ausdrücken. Zum Beispiel könnten wir so etwas sagen wie: „Alle Menüpunkte sollten horizontal ausgerichtet sein, ohne Rand dazwischen. Der erste Menüpunkt sollte sich ohne Rand auf der linken Seite des Menüs befinden.“ Da wir Sätze formuliert haben, um das gewünschte Layout zu erklären, warum können wir sie nicht einfach in unserem Code verwenden? Stellen Sie sich vor, wir könnten den Code so schreiben:

 = Menu = |first menu.item-* is in top left corner of menu |menu.item-* are aligned horizontally next to each other

Tatsächlich handelt es sich dabei um echten Arbeitscode, der aus meinem Projekt kopiert wurde. In diesen letzten beiden Zeilen (beginnend mit dem senkrechten Strich | ) rufen wir benutzerdefinierte Funktionen auf, die ihre Argumente sammeln, indem sie diese beiden Anweisungen analysieren. Natürlich funktioniert das obige Beispiel nicht so wie es ist. Damit es kompiliert werden kann, müssen wir die Handler für diese beiden Anweisungen implementieren. Wir werden später auf diese Implementierung zurückkommen.

Der entscheidende Punkt beim obigen Beispiel ist, dass sich das Testen von Layouts von objektgesteuerten zu ausdrucksgesteuerten Tests verlagert hat. Es mag an einem so kleinen Beispiel nicht offensichtlich sein, aber es ist definitiv in einem größeren Maßstab wahrnehmbar. Also, warum ist das wichtig? Die kurze Antwort lautet, dass dies unsere Denkweise verändert und sich auf die Art und Weise auswirkt, wie wir unsere Software entwerfen und Tests dafür schreiben.

Mit dieser Technik behandeln wir unsere Seite nicht als eine Ansammlung von Objekten mit bestimmten Beziehungen zwischen ihnen. Wir testen die CSS-Eigenschaften einzelner Elemente nicht. Und wir vermeiden es, komplizierten, nicht-trivialen Code zu schreiben. Stattdessen versuchen wir, uns gemeinsame Layoutmuster und aussagekräftige Aussagen auszudenken. Anstatt Menüpunkt 1, Menüpunkt 2 usw. separat zu testen, wenden wir allgemeine Aussagen an, die:

  • auf anderen Elementen reproduzierbar sind;
  • enthalten keine fest codierten Pixelwerte;
  • werden auf Abstraktionen und nicht auf konkrete Objekte angewendet;
  • und nicht zuletzt auch tatsächlich Sinn machen, wenn wir sie lesen.

Wie es funktioniert

Lassen Sie mich den Mechanismus benutzerdefinierter Layoutausdrücke anhand dieses einfachen Beispiels erläutern:

Einfache Skizze
Einfache Skizze (Große Version anzeigen)

In diesem Beispiel sollen wir überprüfen, ob sich die Schaltfläche bis zum Panel erstreckt, ohne Rand auf der linken oder rechten Seite. Ohne benutzerdefinierte Regeln können wir es auf unterschiedliche Weise angehen, aber ich bevorzuge die folgende Lösung:

 button: inside some_panel 0px left right

Der obige Code gibt uns die Flexibilität, einen benutzerdefinierten Rand an den Seiten zu deklarieren, und er testet auch implizit, dass die Schaltfläche vollständig im Panel enthalten ist. Der Nachteil ist, dass es nicht sehr gut lesbar ist, weshalb ich diese Validierung hinter die Ausdrucksschaltfläche setzen werde, die button stretches to some_panel . Damit das funktioniert, müssen wir eine benutzerdefinierte Regel wie diese schreiben:

 @rule %{elementName} stretches to %{parentName} ${elementName}: inside ${parentName} 0px left right

Das ist es. Jetzt können wir es in nur einer Zeile in unseren Test einfügen:

 | button stretches to some_panel

Wie Sie sehen können, akzeptiert diese Regel zwei Argumente: elementName und parentName . Dadurch können wir es auch auf andere Elemente anwenden. Ersetzen Sie einfach die Namen dieser beiden Objekte.

 | login_panel stretches to main_container | header stretches to screen | footer stretches to screen # etc.

Implementierung Ihrer eigenen Testsprache

Kommen wir zurück zu den ersten Beispielen für Layout-Ausdrücke für das horizontale Menü.

 = Menu = | first menu.item-* is in top left corner of menu | menu.item-* are aligned horizontally next to each other

Wir können die erste Regel folgendermaßen implementieren:

 @rule first %{itemPattern} is in %{cornerSides} corner of %{parentElement} @if ${count(itemPattern) > 0} ${first(itemPattern).name}: inside ${parentElement} 0px ${cornerSides}

Wenn es in unserem Beispiel verwendet wird, würde es die Argumente wie folgt analysieren:

  • itemPattern = menu.item-*
  • cornerSides = top left
  • parentElement = menu

Da wir mit unserem ersten Ausdruck fertig sind, können wir zum nächsten übergehen. Im zweiten Ausdruck sollen wir die horizontale Ausrichtung aller Menüpunkte testen. Ich schlage drei einfache Schritte vor:

  1. Finden Sie alle Menüpunkte.
  2. Iteriere über alle bis zum vorletzten Element.
  3. Vergewissern Sie sich, dass sich das Element n links von Element n+1 befindet und dass ihre oberen und unteren Kanten ausgerichtet sind.

Damit es funktioniert, müssen wir eine @forEach -Schleife und Spezifikationen left-of und aligned nehmen. Glücklicherweise können Sie in Galen auf das vorherige oder nächste Element in einer Schleife verweisen. Falls Sie einen Verweis auf das nächste Element deklariert haben, wird es nur bis zum vorletzten Element iterieren, was genau das ist, was wir brauchen.

 @rule %{itemPattern} are aligned horizontally next to each other @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} 0px aligned horizontally all ${nextItem}

Sie fragen sich vielleicht: Was ist, wenn wir in unserem Test einen Rand angeben müssen (z. B. ~ 20px oder 10 to 20px )? Dann schlage ich vor, entweder eine separate Regel zu implementieren oder die bestehende zu erweitern, um ein %{margin} -Argument zu unterstützen.

 @rule %{itemPattern} are aligned horizontally next to each other with %{margin} margin @forEach [${itemPattern}] as item, next as nextItem ${item}: left-of ${nextItem} ${margin} aligned horizontally all ${nextItem}

Das ist es! Wir haben einen generischen Ausdruck erstellt, der uns hilft, das horizontale Menü zu validieren. Aufgrund seiner Flexibilität können wir jedoch noch viel mehr tun. Wir können es verwenden, um alle anderen Elemente auf der Seite zu testen. Wir können es sogar verwenden, um die Ausrichtung von zwei Schaltflächen zu testen:

Formularschaltflächen
Formularschaltflächen (Große Version anzeigen)
 | menu.item-* are aligned horizontally next to each other with 0px margin | submit_button, cancel_button are aligned horizontally next to each other with 20px margin

Sie werden in diesen beiden Beispielen vielleicht bemerken, dass wir das erste Argument auf zwei verschiedene Arten deklariert haben. Im ersten Ausdruck ist das erste Argument “menu.item-*” und im zweiten Ausdruck wird es als “submit_button, cancel_button” . Das ist möglich, weil die @forEach -Schleife es uns ermöglicht, eine durch Kommas getrennte Liste von Objekten zusammen mit dem Sternoperator zu verwenden. Aber wir sind immer noch nicht fertig mit dem Refactoring. Wir könnten den Code weiter verbessern und lesbarer machen. Wenn wir Gruppen für die Menüpunkte und die Schaltflächen des Anmeldeformulars erstellen, könnten wir so etwas erreichen:

 @groups menu_items menu_item-* login_form_buttons submit_button, cancel_button = Testing login page = | &menu_items are aligned horizontally next to each other with 0px margin | &login_form_buttons are aligned horizontally next to each other with 20px margin

In diesem Fall müssen wir das Symbol & verwenden, das für eine Gruppendeklaration steht. Das ist schon mal ein guter Test. Erstens funktioniert es einfach und wir können testen, was wir brauchen. Außerdem ist der Code klar und lesbar. Wenn eine andere Person Sie fragen würde, wie die Anmeldeseite aussehen soll und welche Anforderungen an die Gestaltung gestellt werden, könnten Sie sie bitten, sich den Test anzusehen.

Wie Sie sehen können, ist die Implementierung benutzerdefinierter Ausdrücke für komplexe Layoutmuster keine große Sache. Es mag am Anfang eine Herausforderung sein, aber es ähnelt immer noch so etwas wie eine kreative Aktivität.

Dynamische Margen

Schauen wir uns ein weiteres seltenes Layoutmuster an, das Sie manchmal auf verschiedenen Websites finden. Was wäre, wenn wir testen wollten, ob Elemente den gleichen Abstand zueinander haben? Lassen Sie uns versuchen, eine andere Regel dafür zu implementieren, diesmal jedoch mit einer JavaScript-Implementierung. Ich schlage eine solche Aussage vor: “box_item-* are aligned horizontally next to each other with equal distance” . Dies wird ein wenig schwierig, da wir den Abstand zwischen den Elementen nicht kennen und Pixelwerte nicht einfach fest codieren können. Daher müssen wir als Erstes den tatsächlichen Abstand zwischen dem ersten und dem letzten Element abrufen.

Gleich distanziert
Gleich weit entfernt (Große Version anzeigen)

Sobald wir diese Marge erhalten haben, können wir sie in einer @forEach Schleife deklarieren, ähnlich wie zuvor. Ich schlage vor, diese Regel mithilfe der JavaScript-API zu implementieren, da die erforderliche Logik etwas komplexer ist als in all unseren vorherigen Beispielen. Lassen Sie uns eine Datei namens my-rules.js und diesen Code eingeben:

 rule("%{objectPattern} are aligned horizontally next to each other with equal margin", function (objectName, parameters) { var allItems = findAll(parameters.objectPattern), distance = Math.round(Math.abs(allItems[1].left() - allItems[0].right())), expectedMargin = (distance - 1) + " to " + (distance + 1) + "px"; if (allItems.length > 0) { for (var i = 0; i < allItems.length - 1; i += 1) { var nextElementName = allItems[i + 1].name; this.addObjectSpecs(allItems[i].name, [ "aligned horizontally all " + nextElementName, "left-of " + nextElementName + " " + expectedMargin ]); } } });

In unserem Testcode verwenden wir es wie folgt:

 @script my-rules.js # … = Boxes = | box_item-* are aligned horizontally next to each other with equal distance

Wie Sie sehen können, können wir im Galen Framework bei der Implementierung von Regeln zwischen zwei Sprachen wählen: Galen Specs und JavaScript. Für einfache Ausdrücke ist Galen Specs einfacher zu verwenden, aber für komplexe wähle ich immer JavaScript. Wenn Sie mehr über JavaScript-Regeln erfahren möchten, lesen Sie die Dokumentation.

Galenische Extras

Nachdem ich genug mit verschiedenen Layoutmustern gespielt hatte, wurde mir klar, dass alle diese Galen-Regeln problemlos in jedem anderen Testprojekt angewendet werden könnten. Das brachte mich auf die Idee, die gängigsten Layout-Ausdrücke in einer eigenen Bibliothek zusammenzustellen. So kam es, dass ich das Projekt Galen Extras ins Leben rief. Nachfolgend finden Sie einige Beispiele dafür, wozu diese Bibliothek in der Lage ist:

 | header.icon should be squared | amount of &menu_items should be > 3 | &menu_items are aligned horizontally next to each other | &list_items are aligned vertically above each other with equal distance | every &menu_item is inside menu 0px top and has width > 50px | first &menu_item is inside menu 0px top left | &menu_items are rendered in 2 column table | &menu_items are rendered in 2 column table, with 0 to 1px vertical and 10px horizontal margin | &login_form_elements sides are vertically inside content_container with 20px margin login_panel: | located on the left side of panel and takes 70 % of its width # etc …

Die Galen Extras-Bibliothek enthält viele der Layoutmuster, die Sie normalerweise auf Websites finden würden, und ich aktualisiere sie ständig, sobald ich ein nützliches Muster entdecke. Nachdem diese Bibliothek eingerichtet war, beschloss ich, sie an einem echten Testprojekt auszuprobieren.

Testen der Messaging-App

Derzeit arbeite ich als Softwareentwickler bei Marktplaats. Irgendwann habe ich mich entschieden, all meine gesammelten Erfahrungen in ein reales Projekt einzubringen. Ich musste die Messaging-Seite auf unserer Website testen. So sieht es aus:

Messaging-Seite
Messaging-Seite (Große Version anzeigen)

Um ehrlich zu sein, kam mir das Implementieren von Tests für solche Seiten immer etwas unheimlich vor, insbesondere Layouttests. Aber mit der vorhandenen Galen Extras-Bibliothek lief es eigentlich ziemlich reibungslos, und bald konnte ich diesen Code entwickeln:

 @import ../selected-conversation.gspec @groups (message, messages) messenger.message-* first_two_messages messenger.message-1,messenger.message-2 first_message messenger.message-1 second_message messenger.message-2 third_message messenger.message-3 (message_date_label, message_date_labels) messenger.date_label-* first_date_label messenger.date_label-1 second_date_label messenger.date_label-2 = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ~ 20px margin |&third_message should be located at the right inside messenger with ~ 20px margin |&messages are placed above each other with 10 to 15px margin |text of all &messages should be ["Hi there!", "I want to buy something", "Hello! Sure, it's gonna be 100 euros"] = Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message

Extrahieren von Pixelbereichen

Der Test sah gut aus: Er war kompakt und lesbar, aber noch lange nicht perfekt. Ich mochte all diese Randdefinitionen nicht wirklich ( ~ 20px , 10 to 15px ). Einige von ihnen wurden wiederholt, und es war schwer zu verstehen, wofür jeder von ihnen stand. Deshalb habe ich mich entschieden, jeden Spielraum hinter einer sinnvollen Variablen zu verstecken.

 # ... @set messages_side_margin ~ 20px messages_vertical_margin 10 to 15px = Messages panel = = Messages and Date labels = |amount of visible &message_date_labels should be 1 |first &message_date_label has text is "17 november 2015" |amount of visible &messages should be 3 |&first_two_messages should be located at the left inside messenger with ${messages_side_margin} margin |&third_message should be located at the right inside messenger with ${messages_side_margin} margin |&messages are placed above each other with ${messages_vertical_margin} margin # ...

Wie Sie sehen können, habe ich die Ränder auf messages_vertical_margin und messages_side_margin verschoben. Ich habe auch einen minimal Rand angegeben, der ein Bereich zwischen 0 und 1 Pixel ist.

 # ... @set minimal 0 to 1px = Conversations Panel = | &conversations are aligned above each other with ${minimal} margin # ...

Bildbasierte Validierung und benutzerdefinierte Ausdrücke

Nachdem ich die Positionierung aller Hauptelemente auf der Seite behandelt hatte, beschloss ich, auch das Styling zu testen. Ich wollte überprüfen, ob jede Nachricht eine Hintergrundfarbe hat, die für die Rolle des Benutzers spezifisch ist. Wenn Benutzer angemeldet sind, würden Nachrichten einen hellblauen Hintergrund haben. Nachrichten für andere Benutzer würden einen weißen Hintergrund haben. Falls eine Nachricht nicht gesendet wurde, hat die Fehlermeldung einen rosa Hintergrund. Hier ist die Regel, die mir geholfen hat, diese Stile zu validieren:

 @set OWN_MESSAGE_COLOR #E1E8F5 OTHERS_MESSAGE_COLOR white ERROR_MESSAGE_COLOR #FFE6E6 @rule %{item} should be styled as %{style} message ${item}: @if ${style === "own"} color-scheme > 60% ${OWN_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @elseif ${style === "error"} color-scheme > 60% ${ERROR_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR} @else color-scheme > 60% ${OTHERS_MESSAGE_COLOR}, 0.2 to 20 % ${MAJOR_TEXT_MESSAGE_COLOR}

Die color-scheme überprüft die proportionale Verteilung von Farben innerhalb eines Elements. Es schneidet einen Screenshot der Seite zu und analysiert die Farbverteilung. Um also die Hintergrundfarbe eines Elements zu überprüfen, überprüfen wir einfach, ob seine Verteilung größer als 60 % des gesamten Farbbereichs ist. Im Test wird diese Regel wie folgt aufgerufen:

 = Styling = |&first_two_messages should be styled as others message |&third_message should be styled as own message

Konfigurieren von Testsuiten

Die Messaging-App ist eine dynamische Anwendung, die mit der RESTful Messaging-API zusammenarbeitet. Um seine Layouts in allen verschiedenen Zuständen zu testen, müssen wir daher Testdaten vorbereiten. Ich habe mich entschieden, die Messaging-API zu verspotten, damit ich alle meine Testnachrichten in der Testsuite konfigurieren kann. Hier ist ein Fragment meiner Testsuite, das zeigt, wie unsere Tests strukturiert sind.

 // ... testOnAllDevices("Unselected 2 conversations", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); refresh(driver); new MessageAppPage(driver).waitForIt(); checkLayout(driver, "specs/tests/unselected-conversations.gspec", device.tags); }); testOnAllDevices("When clicking a conversation it should reveal messages", "/", function (driver, device) { mock.onGetMyConversationsReturn(sampleConversations); mock.onGetSingleConversationReturn(sampleMessages); refresh(driver); var page = new MessageAppPage(driver).waitForIt(); page.clickFirstConversation(); checkLayout({ driver: driver, spec: "specs/tests/three-simple-messages-test.gspec", tags: device.tags, vars: { expectedTextProvider: textProvider({ "messenger.message-1": "Hi there!\n11:02", "messenger.message-2": "I want to buy something\n12:02", "messenger.message-3": "Hello! Sure, it's gonna be 100 euros\n13:02" }) } }); }); // ...

Käfer fangen

Die Implementierung dieser einfachen Ausdrücke zahlt sich schnell aus. Sehen wir uns an, welche Art von Fehlern wir mit unserer Testsuite finden können.

Styling-Problem

Hier ist ein Beispiel dafür, wenn etwas in unserer CSS-Codebasis schief geht und als Ergebnis alle Nachrichten mit demselben Hintergrund gerendert werden.

Falsche Hintergrundfarbe der Nachricht
Falsche Hintergrundfarbe der Nachricht (Große Version anzeigen)

Wenn Sie diesen Screenshot mit dem Original vergleichen, werden Sie feststellen, dass die letzte Nachricht einen weißen Hintergrund hat, während sie hellblau sein sollte. Mal sehen, wie Galen dieses Problem meldet:

Screenshot mit Fehlermeldung
Screenshot mit Fehlermeldung (Große Version anzeigen)

Für das hervorgehobene Objekt zeigt es die Fehlerfarbe color #e1e8f5 on “messenger.message-3” is 0% but it should be greater than 60% . Um ehrlich zu sein, sieht diese Fehlermeldung nicht ganz so klar aus, aber da diese Prüfung aus einer benutzerdefinierten Regel generiert wurde, können wir ihren ursprünglichen Namen immer in einem Berichtszweig nachschlagen:

Testfehler melden
Testfehler melden (Große Version anzeigen)

Wenn Sie nach oben scrollen, sehen Sie, dass die ursprüngliche Anweisung &third_message should be styled as own message ist. Dies ist ein weiterer Vorteil der Verwendung benutzerdefinierter Ausdrücke: Sie helfen Ihnen, den Fehler zu verstehen, und beschreiben alle diese generierten Validierungen gut.

Positionierungsproblem

Hier ist ein weiteres Beispiel dafür, wenn das Layout aufgrund einer falschen Ausrichtung eines Elements schief geht. Im folgenden Screenshot sehen Sie, dass die letzte Nachricht auf der linken Seite des Messaging-Ansichtsfensters positioniert ist, anstatt rechts.

Falsche Position der Nachricht
Falsche Position der Nachricht (Große Version anzeigen)

Schauen wir uns noch einmal den Screenshot mit der Fehlermeldung an:

Falsche Position der Nachricht
Falsche Position der Nachricht (Große Version anzeigen)

Der Screenshot hebt den Messaging-Container und das letzte Nachrichtenelement hervor. Zusammen damit zeigt es die folgende Fehlermeldung: “messenger.message-3” is 285px right which is not in range of 22 to 28px . Einem Webentwickler ist möglicherweise nicht klar, warum auf der rechten Seite ein Rand von 22 bis 28 Pixel erwartet wird. Auch hier müssten Sie im Berichtszweig nach einer Validierungsanweisung suchen:

Falsche Position der Nachricht
Falsche Position der Nachricht (Große Version anzeigen)

Die ursprüngliche Anweisung für diese Überprüfung lautet &third_message should be located at the right inside messenger with ~ 25px margin . Das macht viel mehr Sinn. Darüber hinaus werden andere Frontend-Ingenieure diesen Testbericht verstehen, auch wenn sie die Tests nicht geschrieben haben.

Richtlinien für Layouttests

Angesichts all dieser verschiedenen Experimente entschied ich mich, das gesamte Lernen in allgemeine Layout-Testrichtlinien zu formalisieren. Hier ist eine kurze Checkliste mit Schritten, die Sie befolgen sollten, um Ihre Testroutine zu vereinfachen.

  • Identifizieren Sie Layoutmuster im Design.
  • Validierungsaussagen verallgemeinern. Versuchen Sie, die meisten Validierungen in einzelne Sätze zusammenzufassen.
  • Komponieren! Es ist immer besser, Tests wiederholter Elemente in dedizierte Komponenten zu verschieben.
  • Verwenden Sie aussagekräftige Namen für Abschnitte, Regeln und Objekte.
  • Vermeiden Sie Pixel. Versuchen Sie, Pixelwerte (egal ob exakte Werte oder Bereiche) durch aussagekräftige Variablen zu ersetzen.
  • Passen Sie den Code Ihrer Website an, um das Testen zu erleichtern. Dies hilft Ihnen, sowohl Ihren Produktions- als auch Ihren Testcode zu strukturieren und zu pflegen.

Akzeptanzkriterien für die Rettung

Oft bekomme ich Fragen wie: „Wie detailliert sollte ein Layouttest denn sein? Und was sollten wir konkret testen?“ Eine generelle Antwort ist schwer zu geben. Das Problem ist, dass Fehler übersehen werden, wenn die Abdeckung des Tests gering ist. Wenn Sie andererseits zu detaillierte Tests durchführen, erhalten Sie möglicherweise viele Fehlalarme und verlieren sich in Zukunft möglicherweise in der Testwartung. Es gibt also einen Kompromiss. Aber ich habe mir eine allgemeine Richtlinie ausgedacht. Wenn Sie die Arbeit in kleinere User Stories aufteilen, wird es einfacher, das Design einer Seite in Form von Akzeptanzkriterien zu strukturieren. Am Ende könnten Sie genau diese Akzeptanzkriterien direkt in Ihren Testcode aufnehmen. Einige dieser Anweisungen könnten beispielsweise in Form von benutzerdefinierten Regeln definiert werden, wie in allen vorherigen Codebeispielen gezeigt. Ein gutes Beispiel für Akzeptanzkriterien wäre etwa so:

  • Popups sollten vertikal und horizontal auf dem Bildschirm zentriert sein.
  • Sie sollten 400 Pixel breit sein.
  • Schaltflächen sollten horizontal ausgerichtet sein.
  • Und so weiter

Sobald Sie das Design in einfachen Sätzen beschrieben haben, wird es einfacher, sie in wiederverwendbare Anweisungen umzuwandeln und Ihren Testcode zu organisieren.

Fazit

Wie Sie sehen können, hilft Ihnen eine solche Übung dabei, ein Design zu strukturieren und allgemeine Layoutmuster zu entdecken, die von mehreren Seitenkomponenten gemeinsam genutzt werden können. Selbst wenn die Seite komplex ist und aus vielen Elementen besteht, finden Sie immer einen Weg, sie in Ihren Layout-Ausdrücken zu gruppieren. Mit diesem Ansatz wird das Layout-Testen mehr zu einem Werkzeug für die testgetriebene Entwicklung, das Ihnen hilft, Software inkrementell zu entwerfen, zu implementieren und bereitzustellen, was besonders in einer agilen Umgebung nützlich ist.

Ressourcen

  • Galen Framework (offizielle Website)
  • Galen Framework, GitHub
  • Galen Extras (Bibliothek), GitHub
  • Galen Bootstrap, GitHub
  • „Galen Framework-Tutorials“, YouTube