Als wir das letzte Mal über CI/CD geschrieben haben, sind wir kurz darauf eingegangen, wie Tests Ihre Pipeline bereichern, aber auch die größte Ursache für ihre Verzögerung sein können. Der Ratschlag lautete, die Tests zu optimieren und sicherzustellen, dass sie für den jeweiligen Anwendungsfall geeignet sind. Ich dachte mir, ich gehe etwas näher darauf ein und konzentriere mich auf Frontend-Tests, da dies mein Fachgebiet ist und sie in der Vergangenheit einer der größten Engpässe in unserer CI/CD waren.
In diesem Beitrag geht es darum, was wir über Frontend-Tests gelernt haben, welche Fallstricke es gibt und wie man sie vermeiden kann. Lassen Sie uns ins Thema eintauchen.
In diesem Blogbeitrag werden wir hauptsächlich über React sprechen, aber die Ideen sind auch auf andere Frameworks wie Vue, Stencil und Angular übertragbar. Wenn wir also über Komponenten, Hooks und andere Begriffe sprechen, sprechen wir über React.
Beim Frontend-Testing wird die Benutzeroberfläche einer Softwareanwendung verifiziert. Es erfordert Kenntnisse über die Anforderungen in Bezug auf die User Experience und die API-Integration, nicht aber über die Datenbank und die Back-End-Mechanik. Dabei wird getestet, wie Ihre Anwendung dargestellt wird, ob sich alles an der richtigen Stelle befindet und wie sie auf Benutzerinteraktionen reagiert. Es wird auch getestet, ob die Anwendung in den verschiedenen Browsern und auf den verschiedenen Geräten gleich aussieht. Aber nicht nur die Benutzeroberfläche muss getestet werden. Da Frontend-Anwendungen auch Geschäftslogik enthalten können, sollte auch getestet werden, ob die Schnittstelle die richtigen Daten für die jeweilige Situation anzeigt und ob die Integration mit dem Backend korrekt funktioniert. Es gibt viele Bereiche, die man am Frontend testet, nicht nur die Benutzeroberfläche. Deshalb gibt es viele verschiedene Arten von Frontend-Tests.
Wenn Sie Frontend testen, sollten Sie idealerweise keine Details der zugrunde liegenden Implementierung annehmen. Denn Ihre Tests sollten auch dann noch funktionieren, wenn sich die zugrunde liegende Implementierung ändert, die Funktionalität aber unverändert bleibt.
Wenn Sie zum Beispiel eine Komponente mit funktionaler Logik haben, sollten Sie idealerweise die Rendering-Logik von der funktionalen Logik trennen, vielleicht mit einem Hook, wenn Sie React verwenden, und beide getrennt voneinander testen. Ihre Tests sollten sich mit dem Interface befassen, d.h. mit der Ein- und Ausgabe, und nicht mit den internen Zustandsänderungen. Dadurch werden Ihre Tests weiterhin funktionieren, auch wenn sich die Implementierung der Unit ändert, sofern das Interface gleich bleibt.
Frontend-Tests sind etwas ganz anderes als Backend-Tests. Hier hat man es mit sehr vielen zusammenhängenden, dynamischen Bestandteilen und sich asynchron aktualisierenden Komponenten zu tun, die es ziemlich kompliziert machen, eine genaue Vorstellung davon zu bekommen, wie die Abläufe genau funktionieren. Um es noch interessanter zu machen, neigen die Benutzer oft dazu, das System auf „kreative“ Weise zu nutzen, was die Prognose, wie sich das System letztendlich verhält, weiter erschwert. Wenn dann noch Race Conditions und das Mocking globaler Werte wie bei React-Providern hinzukommt, gleicht es einem Wunder, dass wir überhaupt ordentliche Tests schreiben können.
Die Werkzeuge, die wir zum Testen verwenden, versuchen dabei zu helfen, aber sie können auch selbst Teil des Problems sein. Z.B. unterstützen die gemockten Elemente einiger Testbibliotheken nicht die volle API der echten Elemente. Einmal hatte ich das Problem, dass MockedHTMLElelemnt
die setValidity
Methode des echten Elements nicht unterstützte.
Ein weiterer Problempunkt ist die Systemlandschaft: Frontend-Anwendungen können auf Browsern, Smartphones, Tablets und anderen smarten Geräten verwendet werden, nur um hier einige zu nennen. Deshalb muss muss testen, ob sie auf allen relevanten Betriebssystemen und Browsern in den jeweils relevanten Versionen ordnungsgemäß funktionieren.
Aber es gibt Licht am Ende des Tunnels, und Wege, die Ihnen das Leben erleichtern können.
Lassen Sie uns zunächst die unterschiedlichen Arten von Tests definieren, und dann über die verschiedenen Möglichkeiten sprechen, wie man sie in der Praxis einsetzen kann.
Es gibt verschiedene Arten von Tests: Unit-Tests, Integrationstests, visuelle Regressionstests, Cross-Browser-Tests, Barrierefreiheitstests, Akzeptanztests und End-to-End-Tests.
Ein Unit-Test testet die kleinsten funktionalen Bausteine des Quellcode. Das kann eine Komponente, eine Funktion oder ein Hook auf dem Frontend sein. Die Idee dahinter ist zum einen, dass es einfacher ist diese zu testen und zum Anderen ist es in der Regel nur so einigermaßen möglich ist, alle möglichen Anwendungsfälle mit Unit-Tests abzudecken. Dies mit Integrations- und E2E-Tests zu versuchen, wird sehr schnell unübersichtlich. Auf das Warum gehe ich später näher ein, wenn ich darüber spreche, wann man welche Art von Tests schreiben sollte.
Bei Integrationstests werden zwei oder mehr Bausteine oder Module in Ihrem Test kombiniert. Sie könnten beispielsweise mehrere Frontend-Komponenten testen, um zu sehen, wie sie miteinander interagieren. Idealerweise möchten Sie keine unerwarteten Nebeneffekte aufgrund der Kombination mehrerer Units. Per Definition benötigen Integrationstests mehr Zeit als Unit-Tests und müssen eine größere Anzahl von Kombinationen von Auswirkungen berücksichtigen.
Bei E2E-Tests wird die gesamte Anwendung von Anfang bis Ende so getestet, als ob Sie von einem echten Benutzer verwendet werden würden. Das bedeutet, dass alle Teile des Systems vorhanden sein müssen, von der Benutzeroberfläche bis zur Datenbank. Mit anderen Worten, nichts Internes sollte gemockt werden, Sie wollen testen, dass alles so funktioniert, wie es für einen echten Benutzer des Systems funktionieren würde.
Der Begriff End-to-End in E2E-Tests kann zwei Bedeutungen haben, die beide gleichermaßen gültig sind. Es kann sich auf den Beginn eines Workflows bis zu seinem Ende beziehen und es kann sich auch auf die Architektur beziehen, d. h. von der Benutzeroberfläche bis zur Datenbank.
In jedem Fall müssen E2E-Tests dem entsprechen, was Ihre Benutzer in der realen Welt verwenden werden. Alles muss vorhanden sein. Alles bedeutet in diesem Fall alle Bestandteile. Wenn Sie z. B. einen Warenkorb entwickeln, dann könnte ein E2E-Test darin bestehen, zu testen, dass der gesamte Prozess des Kaufens eines Produkts funktioniert. Das bedeutet nicht, dass Ihr Test ein einziger riesiger Test sein sollte, Sie können ihn dennoch in kleineren, besser zu handhabenden Schritten implementieren, die sich ein gemeinsames Setup teilen.
E2E-Tests werden aus der Sicht des Benutzers durchgeführt und betreffen alle Teile eines Systems, vorzugsweise in einer Staging-Umgebung, die mit dem Produktionssystem identisch ist. Dies kann ein echter Staging-Klon des Produktionssystems oder eine Headless-Instanz sein, die Ihr CI/CD-System verwendet. Die einzige Voraussetzung ist, dass Sie ein vollständiges System mit einer funktionierenden API, einem Frontend und einem Backend haben.
E2E-Tests testen echte Geschäftsabläufe, Reloads und alles, um sicherzustellen, dass die Gesamtergebnisse der Benutzerführung den erwarteten Ergebnissen entsprechen.
Die obigen Beispiele sind die drei grundlegenden Arten von Ttests. Andere Frontend-spezifische Arten können unter Unit- und Integrationstests fallen, und ich führe sie hier separat auf, weil sie einige Stolperfallen aufweisen, auf die Sie achten sollten.
Unit-Tests haben keine Augen, so dass sie subtile Änderungen an der Benutzeroberfläche, wie z. B. eine Veränderung von ein paar Pixeln aufgrund einer seltsamen CSS-Interaktion, nicht erfassen können. Bei visuellen Tests nehmen Sie einen Schnappschuss Ihrer Benutzeroberfläche auf und vergleichen ihn mit einem Referenzbild.
Usability-Tests prüfen, ob Ihre Benutzeroberfläche einfach zu bedienen ist. Eine besondere Form des Usability-Tests ist der Barrierefreiheitstest, bei dem geprüft wird, ob beeinträchtigte Menschen, die Computer mit Hilfsmitteln benutzen, auf Ihre Benutzeroberfläche zugreifen können, z. B. Menschen, die einen Bildschirmlesegerät/Screenreader benutzen. Heutzutage ist so gut wie alles automatisiert, so dass es Werkzeuge wie Lighthouse gibt, die die Benutzerfreundlichkeit Ihrer Anwendung bewerten und Vorschläge zur Verbesserung der Benutzeroberfläche machen.
Da Webanwendungen von verschiedenen Geräten aus aufgerufen werden können, vom normalen Browser auf einem Computer bis hin zum Display eines intelligenten Kühlschranks, müssen Sie testen, ob sie auf allen relevanten Geräten richtig funktionieren. Wie bereits erwähnt, ist so gut wie alles automatisiert, es gibt also Werkzeuge, die diese Aufgabe für Sie übernehmen. Und Sie brauchen keinen physischen Zugang zu jedem Gerätetyp, denn wir sind inzwischen ziemlich gut bei der Virtualisierung.
Die verschiedenen Testarten auf dem Frontend haben alle ihre Vor- und Nachteile. Es gibt viele Modelle, die versuchen, diese Vorteile herauszustellen, aber ich werde sie hier nicht verwenden.
Wir wollen es einfach halten und nach Kategorien aufschlüsseln.
Wie schnell wird ein Test ausgeführt?
Wenn es um Geschwindigkeit geht, haben Unit-Tests die Nase vorn. Sie testen einzelne Bausteine und erfordern den geringsten Konfigurationsaufwand.
Integrationstests sind tendenziell langsamer als Unit-Tests und E2E-Tests laufen am langsamsten. Dies ist ein allgemeines Prinzip: Unit-Tests und E2E-Tests befinden sich immer an den entgegengesetzten Enden des Spektrums, und Integrationstests sind etwas dazwischen.
Wie einfach sind die Tests zu warten?
Auch hier gilt, dass Unit-Tests aufgrund ihres geringen Umfangs leichter zu warten sind. Integrationstests sind etwas schwieriger zu warten, da sie mehrere Bausteine integrieren, und E2E-Tests sind am aufwendigsten zu warten.
Wie zuverlässig ist ein Test? Wenn Sie ihn 50 Mal durchführen, erhalten Sie dann 50 Mal das selbe Ergebnis?
Diese Unsicherheit wird als Unbeständigkeit (Flakiness) bezeichnet. Sie wird umso größer, je mehr Bestandteile in einem Test verwendet werden. Während Unit-Tests öfter unbeständig sein können, ist die Wahrscheinlichkeit für Unbeständigkeit bei Integrationstests und E2E-Tests höher.
Wie nah sind die Tests an der Wirklichkeit?
Seien wir ehrlich, die zwei vorhergehenden Kategorien haben Unit-Tests so dargestellt, als ob diese alles besser machen würden, oder? Aber diese Vorteile gehen auf Kosten der Realitätsnähe. Unit-Tests können diese Dinge leisten, weil Sie einzelne Beuteile isoliert testen. Aber das ist nicht die Art und Weise, wie sie verwendet werden. In Bezug auf die Realitätsnähe haben Unit-Tests also die schlechtesten Werte, Integrationstests liegen in der Mitte und E2E-Tests sind am nächsten an der Realität.
Wieviel kostet es, einen Test auszuführen?
„Die Kosten für alles, was mit der Entwicklung zu tun hat, werden in Dollar oder Stunden gemessen.” Diesen Satz oder eine Abwandlung davon habe ich in der Vergangenheit schon mehrfach verwendet, und er ist auch hier gültig. In Bezug auf die Kosten sind Unit-Tests die billigsten, Integrationstests sind immer noch das mittlere Kind, und E2E-Tests sind die teuersten. Dabei kann es sich um den Dollarwert handeln, den Sie zahlen müssen, um Ihre Tests auf Ihrem CI/CD-System laufen zu lassen, oder um die Zeit, die der Test für die Ausführung benötigen würde.
Tatsächlich ist der Unterschied zwischen Unit-Tests und E2E-Tests so groß, dass Sie es sich in manchen Fällen leisten können, Hunderte von Unit-Tests in der gleichen Zeit und zu einem Bruchteil der Kosten auszuführen, die für einen einzigen E2E-Test erforderlich wären.
Welche Tests sollten Sie am meisten schreiben? Mein Vorschlag ist, dass Sie die meisten Tests als Unit-Tests schreiben, da diese meistens den größten Nutzen im Vergleich zum eingebrachten Aufwand bringen. Integrationstests sollten weniger sein als Unit-Tests, und E2E noch weniger als diese.
Bei Unit-Tests gibt es ein paar typische Fallstricke, auf die Sie achten sollten. Da dies die häufigste Testart ist, werden wir uns hier am meisten Zeit nehmen.
Der erste Fehler ist das Testen von Implementierungsdetails, anstatt das Verhalten zu testen. Das ist ein Problem, weil es Ihre Tests an die jeweilige Implementierung koppelt, und wenn sich diese Implementierung ändert, wird der gesamte Test hinfällig. Dies gilt nicht nur für Frontend-Tests, so dass man es sich merken sollte, auch wenn man nicht am Frontend arbeitet.
Der Tipp hier ist, das Verhalten eines Bausteins zu testen, und nicht wie er implementiert ist. Das bedeutet zunächst, dass Sie Ihren Code so schreiben sollten, dass er testbar ist. Dies ist kein reines Test-Thema, aber es ist relevant, weshalb wir es mit aufgreifen. Gut strukturierter Code ist leichter zu testen als unsauberer Code.
Zweitens: Versuchen Sie, beim Testen nicht auf den internen Zustand einer Komponente zuzugreifen. Dies wird macnhmal auch als Blackbox-Test bezeichnet. Stellen Sie sich Ihren Baustein oder Komponente als eine Blackbox vor. Sie wissen nicht, was sich im Inneren befindet, also testen Sie nur, wie die Box mit der Außenwelt interagiert. Wenn Sie zum Beispiel eine Dropdown-Komponente haben, testen Sie, was passiert, wenn der Benutzer darauf klickt, d. h. ein Dropdown wird angezeigt, anstatt zu prüfen, ob sich der Zustand auf die eine oder andere Weise ändert.
Verhalten zu testen bedeutet, dass Ihre Tests auch dann noch relevant sind, wenn sich die Implementierungsdetails ändern. Wenn Sie also zum Beispiel eine Hook erstellen, würde das Testen des Verhaltens bedeuten, dass wenn Sie ihr x
übergeben, diese y
zurückgibt. Wenn sich das Innenleben des Hooks ändert, sollte die Eingabe x
immer noch y
zurückgeben.
Es ist sehr leicht, in die Falle von großen Tests zu tappen, die zu viele Dinge aufeinmal testen. Das ist problematisch, denn es verschleiert Fehler. Große Tests sind außerdem schwerer zu lesen und zu verstehen. Wenn sie fehlschlagen, ist es schwieriger herauszufinden, warum, und noch schwieriger, sie anzupassen.
Machen Sie also Ihre Unit-Tests möglichst einfach und klein. Tests sollten eine einzige Sache testen, nicht mehrere. Es ist besser, viele kleine Tests zu haben als einen großen Test.
Namensgebung ist eine der schwierigsten Angelegenheiten in der Software-Entwicklung, und das gilt auch für Tests. Es ist leider sehr leicht, beim Schreiben von Tests auf unpassende oder missverständliche Namen zurückzugreifen. Wenn man sich die Liste der Tests ansieht und nicht sofort erkennen kann, was die Anwendung tut, dann hat man unklare Namensgebung.
Der Trick besteht darin, sachliche und beschreibende Namen zu verwenden, und dabei nicht zu kreativ zu werden oder gar abzukürzen. Die Namen sollten verraten, was getestet wird und was das erwartete Ergebnis ist.
Das hier ist beschreibend:it("clicking button navigates to the homepage");
Das hier nicht:it("homepage");
Wir haben diesen Punkt bereits im Zusammenhang mit CI/CD angesprochen, aber eine häufige Fehlerquelle ist, dass nur der erfolgreiche Pfad geprüft wird. Tests, die dies tun, sind im besten Fall irreführend und im schlimmsten Fall gefährlich. Sie sollten nie nur testen, ob etwas gut läuft, sondern auch, was passiert, wenn etwas schief läuft. Sie sollten auch Fehlerfälle und Grenzfälle testen.
Testen Sie alle Fälle, die Ihnen einfallen. Wenn Sie einen Programmfehler im Code finden, sollten Sie einen Test für diesen Fall hinzufügen, damit es nicht zu einem Rückfall kommt. Es reicht nicht aus, den erfolgreichen Pfad zu testen.
Ein weiterer Teil, der oft übergangen wird, sind Fehlerbehandlung und Exceptions. Wenn Ihre Komponente über eine Validierungs- oder Fehlerbehandlungslogik verfügt, dann sollte auch diese getestet werden.
Stellen Sie sich folgendes Szenario vor: Sie schreiben mehrere Tests für eine Komponente, alle Tests sind erfolgreich, und die Welt ist in Ordnung. Dann wollen Sie einen neuen Testfall hinzufügen, Sie fügen diesen einen Test hinzu und 5 andere, nicht damit verbundene Tests schlagen plötzlich fehl. Aber wenn Sie diesen Test entfernen oder die Reihenfolge ändern, sind sie diese wieder erfolgreich.
Was hier passiert, ist, dass Ihre Tests nicht richtig voneinander isoliert sind und sich gegenseitig beeinflussen. Dies kann auf Mocks zurückzuführen sein, die nicht zurückgesetzt werden, auf unbehandelte asynchrone Operationen, auf Tests, die einen Teil des Zustands ändern, usw. - es gibt erstaunlich viele Gründe hierfür.
Sich gegenseitig beeinflussende Tests können Ihnen ein falsches Gefühl der Sicherheit vermitteln und sind sehr schwierig zu debuggen. Wenn Sie also Mocks verwenden, setzen Sie diese zurück, oder besser noch, tun Sie es vor jeder (beforeEach) Testausführung. Stellen Sie sicher, dass Sie Mocks nach jedem (afterEach) Test zurücksetzen oder wiederherstellen. Wenn Sie asynchrone Funktionen testen, dann warten Sie (waitFor) auf sie, wenn Sie Zeitgeber verwenden, dann verwenden Sie useFakeTimers und wenn Sie die Zeit haben, führen Sie jeden Test separat aus.
Weiter geht es mit den Integrationstests. Hier sind folgende Dinge von Bedeutung.
Es liegt in der Natur der Sache, dass Integrationstests mehr Setup- und Teardown-Funktionalität erfordern als Unit-Tests, und das kann zum Problem werden, wenn dieses Setup zu komplex wird.
Der Tipp lautet hier, zu automatisieren, wo es möglich ist, und zu mocken, wo es erforderlich ist. Da Integrationstests näher an der Realität sein müssen, brauchen Sie im Prinzip weniger Mocks als bei Unit-Tests. Es geht nicht darum, weniger Mocks zu haben, denn die Anzahl der gemockten Objekte ist kein guter Indikator, sondern vielmehr darum, dass die integrierten Bausteine so nah wie möglich an realistischen Arbeitsszenarien sind. Wenn Sie also testen, dass 3 Bausteine oder Komponenten zusammenarbeiten, dann ist es in Ordnung, 5 andere zu mocken, die nicht an der getesteten Interaktion beteiligt sind.
Da Integrationstests schnell kompliziert werden können, wird die Regel, sie klein zu halten, in „Halte sie klein, wo es praktisch ist“ geändert. Wenn Sie zum Beispiel ein langes Setup haben, könnte es besser sein, es einmal zu vorzubereiten und anschließend mehrere Testfälle durchzuführen, anstatt das Setup zu wiederholen. Sie könnten mehrere Tests durchführen, die das gleiche Setup verwenden, oder mehrere Assertions in einem Test einsetzen. Wenn Sie mehrere Assertions einsetzen, stellen Sie sicher, dass diese das selbe Verhalten betreffen. Sie wollen nicht, dass ein einzelner Test zwei oder mehr nicht zusammenhängende Funktionalitäten testet.
Seien Sie sich auch bewusst, dass ein gemeinsames Setup dazu führen kann, dass ein Test einen anderen beeinflusst, so dass Sie manchmal keine andere Wahl haben, als ein neues Setup durchzuführen. Im besten Fall reicht es aus einige Mocks zurückzusetzen.
Es mag seltsam klingen, aber es gibt so etwas wie „over mocking“. Da bei Integrationstests getestet wird, wie gut die Bausteine oder Dienste miteinander zusammenspielen, empfehlen wir, nach Möglichkeit realistische Daten zu verwenden. Es ist zwar in Ordnung, nicht zusammenhängende Bausteine zu mocken, aber versuchen Sie auf jeden Fall, die zu testenden Bausteine so realitätsnah wie möglich zu gestalten.
Integrationstests sind in der Regel umfangreicher und benötigen mehr Zeit als Unit-Tests. Sie sind auch schwieriger zu warten, so dass das Testen von Dingen, die auf Unit-Ebene durchgeführt werden können, hier möglicherweise nicht der beste Ansatz ist. Wenn Sie zum Beispiel einen Integrationstest für einen Abschnitt durchführen, könnten Sie einige gültige und einige ungültige Eingaben machen, aber das Testen, wie die Bausteine mit allen Sonderfällen umgehen, sollte besser auf der Unit-Ebene erfolgen. Es wird immer bestimmte Überschneidungen bei den Testszenarien geben, aber das ist ganz normal und sogar teils erwünscht.
Es mag trivial klingen, aber es muss gesagt werden. Die Systemumgebung, in der Sie Ihre Tests durchführen, sollte lokal, in Ihrer CI/CD-Pipeline UND in Produktion identisch sein. Es gibt nichts ärgerlicheres als einen Test, der lokal funktioniert, aber bei CI/CD fehlschlägt. Sie wollen keine Tests, die nur auf dem Rechner eines Entwicklers funktionieren, aber auf einem anderen nicht.
Docker ist hier eine große Hilfe, also verwenden Sie Docker lokal und auf Ihrem CI/CD. Wir schlagen Docker vor, weil wir Docker lieben, aber ganz ehrlich, jedes Tool, mit dem Sie reproduzierbare Entwicklungsumgebungen erstellen können, funktioniert genauso. Wenn Sie also ein anderes Tool bevorzugen, das die Arbeit erledigt, dann nur zu.
Ein häufiger Fallstrick bei visuellen Tests ist, dass sie schon bei sehr kleinen Änderungen unzuverlässig erscheinen können. Nehmen wir an, Sie aktualisieren Ihr CSS und das Layout verschiebt sich um ein Pixel. Wenn Ihre Schwelle für Änderungen niedrig genug ist, werden Ihre Tests fehlschlagen. Ich persönlich bin bei 1-Pixel-Änderungen nicht so streng, weil ich der Meinung bin, dass die Benutzer den Unterschied nicht erkennen können und die Änderung nicht groß genug ist, um den Zeitaufwand für die Korrektur zu rechtfertigen.
Der Tipp ist also, die Schwelle, ab der eine Änderung als solche definiert wird, auf ein vernünftiges Niveau einzustellen. Natürlich ist dies subjektiv, und wenn Sie pixelgenau sein müssen, dann könnte eine sehr niedrige Schwelle für eine Änderung sehr nützlich sein.
Die Anzahl der visuellen Tests kann sehr schnell ansteigen. Wenn Sie z. B. 20 Komponenten testen, kann es sein, dass Sie separate Tests für den erfolgreichen Pfad, Randfälle und Fehler haben. Das ist eine Menge zu prüfender Snapshots. Wenn ein Entwickler eine zu große Änderung vornimmt, kann dies dazu führen, dass viele Tests nicht mehr funktionieren, und die Überprüfung aller Tests kann sehr zeitaufwändig sein. Entwickler sind auch nur Menschen und es kann passieren, dass sie die Referenzbilder neu generieren, ohne diese wirklich alle zu überprüfen.
Um dies zu vermeiden, sollten Sie immer versuchen, die Änderungen klein zu halten. Das bedeutet nicht, dass alle Ihre Änderungen klein sein sollten, aber Sie sollten, wenn Sie eine Änderung vornehmen, Ihre Snapshots an Ort und Stelle überprüfen und gegebenenfalls anpassen. Im Grunde genommen sollten Sie den Entwicklungs-/Test-Iterationszyklus konsequenter umsetzen.
Browser handhaben CSS unterschiedlich, das ist eine Tatsache, die Sie akzeptieren müssen. Dieselbe Seite wird von Browser zu Browser etwas anders aussehen. Ein Fehler besteht darin, verschiedene Browser nicht in Ihre visuellen Tests einzubeziehen.
Es lässt sich darüber streiten, ob dies schlecht ist und gegen Cross-Browser-Tests im Allgemeinen spricht, da wir lediglich die verschiedenen Browser auf ihre Eigenheiten hin testen, aber ich denke, dieser Streitpunkt sollte vorerst zurückgestellt werden.
Der Ratschlag hier ist, Ihre visuellen Tests auf mehreren Browsern und Geräten durchzuführen. Glücklicherweise unterstützen viele Test-Runner diese Funktionalität, daher sollten Sie sicherstellen, dass Sie Ihre Zielbrowser und -geräte entsprechend konfiguriert haben.
Mobile Tests konzentrieren sich oft auf eine Bildschirmausrichtung und ignorieren das Konzept, dass Benutzer ihr Gerät drehen könnten. Das hat in der Regel keine schlimmen Folgen, aber es ist eine gute Praxis, unterschiedliche Ausrichtungen in Ihre Tests einzubeziehen.
Ein Fallstrick in diesem Zusammenhang ist, dass dies, wie auch das Testen von Browsern oder Auflösungen, die Anzahl der Tests, die Sie durchführen müssen, drastisch erhöhen kann. Bei 10 Komponenten, über 5 Auflösungen und 2 Ausrichtungen haben Sie plötzlich 100 Testszenarien.
Sie sollten auch alle dynamischen Inhalte in die Tests einbeziehen. Wenn Ihre Benutzeroberfläche dynamische Inhalte hat und Sie dies beim Schreiben von Tests nicht beachten, werden Sie unzuverlässige visuelle Tests haben. Hier ein triviales Beispiel: eine Komponente, die die aktuelle Uhrzeit und das Datum anzeigt. Der Test wird jedes Mal fehlschlagen, wenn Sie ihn ausführen.
Der Trick dabei ist, solche Daten so zu mocken, so dass sie immer gleich sind. Ich würde auch für das Mocking von externen Bildern und externen Inhalten wie CMS- Texten plädieren.
Wenn Sie all die oben genannten Punkte beachtet haben, werden Sie eine Menge Tests haben, und wenn Sie ein externes CI/CD-System verwenden, werden die Kosten erheblich steigen. Es kann so problematisch werden, dass visuelle Tests ehrlich gesagt die teuerste Art von Tests werden können. Und in Anbetracht des Geschwindigkeitsaspekts kann es der größte Beitrag zur Verzögerung Ihrer CI/CD-Pipeline sein.
Ich weiß, es mag seltsam klingen, nachdem ich die letzten Minuten damit verbracht habe, darüber zu sprechen, wie Sie das meiste aus Ihren visuellen Tests herausholen können, um dann plötzlich umzuschwenken, aber so ist es eben. Sie sollten nach bestem Wissen und Gewissen entscheiden, was Sie visuell testen wollen. Jede Komponenten auf mehrere Arten visuell zu testen ist oft nicht der beste Ansatz. Es gibt keine allgemeingültige Lösung, jedes Projekt ist anders, Sie müssen dies im Einzelfall entscheiden. So könnten Sie zum Beispiel visuelle Tests für Ihre Homepage durchführen, aber nicht für die Verwaltungsseite. Auch diese Entscheidung können nur Sie treffen.
Meiner Meinung nach sind die meisten Probleme bei der Prüfung der Barrierefreiheit darauf zurückzuführen, dass es keine Strategie für die Barrierefreiheit gibt. Wenn man diese nicht von Anfang an als Anforderung hat, kann man sie nur allzu leicht ignorieren.
Ein Problem sind zum Beispiel fehlende Alt-Texte bei Bildern. Wenn Sie jedoch eine Strategie haben, die dies als Anforderung festlegt, können Sie Ihren Linter so konfigurieren, dass Bilder ohne Alt-Tag markiert werden.
Automatisierten Barrierefreiheitstests wird oft nicht so viel Aufmerksamkeit geschenkt wie anderen Tests, was dazu führt, dass sie oft vernachlässigt werden.
Daher mein Rat: Haben Sie einen Strategie, und verwenden Sie lighthouse oder etwas Ähnliches.
E2E-Tests sind per Definition sehr umfangreich und können sehr viel Zeit in Anspruch nehmen. Manche Leute ziehen es vor, sie ganz zu vermeiden, aber ich denke, wenn man sie haben muss, sollte man strategisch überlegen, wann man sie braucht. Es könnte sinnvoller sein, sie nur bei der Auslieferung an einen Testserver laufen zu lassen, als immer dann, wenn ein Pull Request erstellt oder geändert wird.
Die Entwicklung von E2E-Tests kann kompliziert sein. Ein Fallstrick ist, sich auf die falschen Dinge zu konzentrieren. E2E-Tests sollten sich nicht auf Ihre Anwendung, d. h. auf die Implementierung, sondern auf den Endbenutzer konzentrieren. Idealerweise sollten Sie nur besonders wichtige Pfade in diese Tests einbeziehen, d. h. Sie sollten sich darauf konzentrieren, wie die Endbenutzer das System verwenden, um ein bestimmtes und klares Ziel zu erreichen.
Wenn Sie sich erinnern, haben wir gesagt, dass Sie mehr Unit- als Integrationstests und mehr Integrationstests als E2E-Tests schreiben sollten. Machen Sie sich also nicht verrückt und schreiben Sie eine Million E2E-Tests, sondern konzentrieren Sie sich auf das, was den größten Nutzen bringt.
Das Testen ist ein wichtiger Teil der Entwicklung, und es ist ein großes Thema, auch wenn wir uns nur auf das Frontend konzentrieren. Ich kratze ehrlich gesagt nur an der Oberfläche, worauf man achten muss, und ich werde wahrscheinlich irgendwann mehr über das Thema schreiben, mit mehr Fokus auf die einzelnen Aspekte. Wenn Sie daran interessiert sind, können Sie unserer LinkedIn-Seite folgen, um kein Update zu verpassen, und wenn Sie mit einem Partner zusammenarbeiten möchten, der sich mit Tests auskennt, kontaktieren Sie uns gerne.