Seitdem wir Deck.gl verwenden (ein vielseitiges und zuverlässiges Visualisierungs-Framework für große Datensätze auf WebGL-Basis), wollten wir uns selbst herausfordern. Wir versuchten einen Weg zu finden um die gerenderten Maps, welche dem Nutzer zur Verfügung gestellt werden, zu testen und sehen ob die Daten unseren Erwartungen entsprechen. Da Cypress so ziemlich das ideale Tool für diese Art von End-to-End-Tests ist, haben wir zunächst analysiert, was bereits an Anwendung vorhanden ist, wo die Grenzen (leider gibt es einige) dieser Anwendungen liegen und ob es vielversprechend ist (lange Rede, kurzer Sinn: das ist es definitiv!).
Wenn wir uns die Codebasis einer beliebigen Deck.gl-basierten Webanwendung ansehen, finden wir mehrere interessante Punkte. Zuallererst sind mindestens 50 % aller Anwendungsfunktionen speziell für die Visualisierung und Interaktion mit 2D- oder 3D-Objekten auf einer bestimmten Map geschrieben. Wenn wir testen möchten, wie sich die App, die Map oder diese Objekte verhalten, gibt es bereits eine offiziell unterstützte Methode - den SnapshotTestRunner von Deck.gl -, die wir jedoch als ziemlich unzureichend empfinden, da sie nur Tests in einem "Nur-Lesen"-Modus ermöglicht. Wenn Ihre Anwendung, wie unsere, Funktionen wie Drag-and-Drop, Objektauswahl und Live-Objektbearbeitung unterstützt, dann werden Sie zu dem Schluss kommen, dass etwas Fortgeschritteneres als Snapshot-Tests erforderlich ist.
Ein weiterer Nebeneffekt, wenn man eine solche Webanwendung nicht testen kann, ist die extrem niedrige Codeabdeckung. Einfach ausgedrückt: Wenn es keine Möglichkeit gibt, Tests zu schreiben, die die oben genannten fortgeschrittenen Funktionen abdecken, kann niemand behaupten, dass seine Deck.gl-Anwendung eine gute Codeabdeckung hat.
Cypress ist eine der vielseitigsten und anpassungsfähigsten Lösungen auf dem Markt und wird von unserer QA für die Implementierung von End-to-End-Tests verwendet. Das gilt auch für unsere Deck.gl-Anwendung, und da die Anwendung selbst sehr umfangreich ist, wurden die anderen 50 % der Funktionalitäten bereits durch Cypress-Tests abgedeckt (diejenigen, die sich nicht mit den Deck.gl-Funktionen befassen).
An diesem Punkt wollten wir herausfinden, wie wir all diese fortgeschrittenen Funktionen implementieren können, ohne zusätzliche Test-Frameworks einrichten oder verwenden zu müssen, damit wir uns keine Sorgen um den Overhead machen müssen.
Die Antwort auf diese Frage liegt in der einfachen Tatsache, dass Deck.gl WebGL-Technologien und -Funktionen verwendet. Mit einer solchen Implementierungsstrategie im Hinterkopf wissen wir, dass keines der Elemente, die wir auf dem Bildschirm sehen, tatsächlich DOM-Elemente sind.
Die Elemente, die wir auf einer Deck.gl-Karte rendern wollen, werden tatsächlich in ein Canvas-Element übertragen, was das Abrufen dieser Elemente ziemlich schwierig macht. Sobald alle 2D/3D-Objekte auf der Leinwand (Szene) gerendert sind, sind sie versteckt und unzugänglich (mit anderen Worten, Sie können sie nicht sehen, selbst wenn Sie explizit über das Fenster "Element inspizieren" nach ihnen suchen). Wenn dies der Fall ist, kann nicht einmal Cypress die Objekte abrufen und ihre Existenz überprüfen.
Zu den interaktiven Funktionen unserer Anwendung gehören Aktionen wie das Aktivieren/Deaktivieren einer Ebene (Gruppe von Objekten) in der Szene, das Bewegen eines Objekts in der Szene oder das Bewegen eines Objekts mit der Maus, um einen Tooltip anzuzeigen. Die automatische Validierung dieser Funktionen bedeutet, dass wir Informationen darüber erhalten müssen, ob ein Objekt derzeit in der Szene angezeigt wird, wo es angezeigt wird, in welcher Form, Farbe, usw. Generell brauchten wir eine Möglichkeit, eine Szene zu untersuchen, eine Möglichkeit, ein Objekt zu finden (cy.get alternative) und seine Eigenschaften zu erhalten.
Aus der Code-Perspektive kann die Introspektion einer Szene als Zugriff auf den WebGL-Kontext, seinen internen Zustand und die damit verbundenen Operationen gesehen werden. Leider ist die einfache WebGL-API eher zu niedrig angesiedelt. Da wir eher geschäftsorientiert als forschungsorientiert arbeiten, haben wir uns überlegt, die Lösung auf eine höhere Ebene zu beschränken - Deck.gl. Auf diese Weise waren wir in der Lage, einen funktionierenden Prototyp zu erstellen und die Ergebnisse viel früher zu sehen.
Im Allgemeinen ist es eine gute Praxis, den Produktionscode mit Tests zu versehen, aber noch wichtiger ist, dass die zum Schreiben der Tests verwendeten Tools auch wie vorgesehen funktionieren.
Unsere wichtigsten Annahmen waren:
Nachdem wir die Grundvoraussetzungen geschaffen haben, schauen wir uns die höhere Ebene an - die Interaktion zwischen der App und der Deck.gl-API. Die Lösung stützt sich auf die von Deck.gl angebotene öffentliche API und nutzt ein wenig der internen API von Deck.gl, um die Ebenenhierarchie abzurufen. Wenn wir nun ein Stück Code in die Mitte zwischen Deck.gl und der Anwendung einfügen, sollten wir in der Lage sein zu beobachten, wie die Anwendung den Input von Deck.gl manipuliert und den Status von Deck.gl abruft, um die Eigenschaften aller Objekte zu überprüfen. In der Praxis können wir dies erreichen, indem wir Props injizieren, Deck.gl-Instanzen abfangen, alle notwendigen internen Strukturen für ein schnelleres Auffinden von Objekten aufbauen und schließlich Dienstfunktionen für den Betrieb des Systems exportieren. Diese exportierten Funktionen sind der Kern unseres Plugins, eine API, die jeder QA-Ingenieur in seinen automatisierten Tests verwenden kann.
Unsere Arbeit, um dies zum Laufen zu bringen, begann mit dem Schreiben des "Backend"-Teils, was bedeutete, ein Plugin im Quellcode bereitzustellen, das als Middleware zwischen unserer benutzerdefinierten App und Deck.gl selbst fungiert. Es sollte die erforderlichen Eigenschaften der App empfangen, sie durch die Einrichtung von Proxy-Event-Handlern verarbeiten und neue Props zurückgeben, die schließlich an Deck.gl weitergeleitet werden.
Dieser Teil wurde durch die Bereitstellung von Test-IDs für alle Ebenen, die wir auf der Map darstellen, erreicht. Wenn Sie jemals Deck.gl-Ebenen debuggt haben, wissen Sie, dass es keine solche Eigenschaft wie testId gibt, aber wenn Sie eine benutzerdefinierte TypeScript-Schnittstelle an die Deck.gl-Ebene mit dieser neuen String-Eigenschaft weiterleiten, können Sie Getter und Setter ohne Probleme schreiben.
Nachdem dies fertig war, mussten wir uns eine Möglichkeit überlegen, etwas in das Fensterobjekt des Browsers zu exportieren, damit Cypress auf die gerenderten Daten zugreifen kann. Daher haben wir eine Instanz unserer benutzerdefinierten DeckInspector-Klasse erstellt, die wir exportieren, sobald die Map mit ihren Daten gerendert wird.
Von diesem Zeitpunkt an kann Cypress auf die Funktionen der Klasse zugreifen, indem es window.deckInspector aufruft. Der Rest ist dann Aufgabe der Tester. Einige unserer erfolgreich implementierten und getesteten Funktionen, die von unserer DeckInspector-Klasse bereitgestellt werden, sind getElementByTestId(testId), zoomTo(element), getText(element) und getLayerHierarchy(), das die komplette Hierarchie aller auf der Map sichtbaren Ebenen abruft. Sobald das Plugin fertig ist, werden auch Funktionen zum Abrufen bestimmter Ebenenmerkmale verfügbar sein (z. B. dglFillColor oder dglCenter).
Obwohl es sich um unsere erste Proof-of-Concept-Version handelt, sind uns nicht viele offene Punkte aufgefallen, die nicht behoben werden können. Die beiden größten Probleme waren für uns das Ziehen und Ablegen von Elementen auf die Map / Leinwand sowie das Abrufen der Koordinaten des Mittelpunkts einer gerenderten Ebene. Tooltips, die ebenfalls ein offener Punkt sind, wurden noch nicht behandelt oder daraufhin untersucht, ob sie testbar sind.
Nicht zuletzt sollten, sobald das Plugin öffentlich verfügbar ist, auch Daten zur Testabdeckung des Plugins selbst bereitgestellt werden.
Wir haben nur an der Oberfläche gekratzt, und dennoch sind wir sehr optimistisch, was die Zukunft dieses Ansatzes angeht, und wie viel Potenzial er trotz seiner Einschränkungen jetzt schon bietet! Wir hoffen, dass Ihnen die Lektüre dieses Artikels ebenso viel Spaß gemacht hat wie uns die Recherche und das Schreiben über Cypress und Deck.gl!