Es wurde bereits viel über Unit-Tests für React-Komponenten, Redux-Reducer und Ähnliches geschrieben, doch es fiel uns sehr schwer, fundierte Beiträge über die Einrichtung geeigneter Full-Stack-Integrationstests für den modernen JS-Stack zu finden.
Dies ist der erste Artikel einer vierteiligen Serie:
- Nightwatch und API-Aufrufe für saubere, kompakte und isolierte Full-Stack-Tests
- Gulp, AWS und Terraform für müheloses Continuous Delivery
- Circle CI und Saucelabs – für die Automatisierung aller Prozesse
Unsere Konfiguration war besonders knifflig, da wir eine Handvoll vollständig entkoppelter Backend-Dienste und zwei React-Apps haben, die sich viel Code teilen. Falls Sie einen ähnlichen Weg einschlagen, hilft Ihnen diese Serie hoffentlich dabei, ein paar Schritte zu sparen.
Nightwatch und API-Aufrufe für saubere, kompakte und isolierte Full-Stack-Tests
Das Ziel ist es, eine Reihe von Tests zu erstellen, die das Zusammenspiel zwischen den Frontend-Anwendungen und den Backend-Diensten wirklich auf Herz und Nieren prüfen. Da wir zudem Nutzer auf der ganzen Welt haben, die Chrome, Firefox und IE unter Windows und Mac verwenden, wollen wir sichergehen, dass bei keiner der von uns unterstützten Kombinationen aus Browser, Betriebssystem und Bildschirmgröße Fehler auftreten.
Hier ist eine Debug-Vorschau dessen, was wir bei jedem Push auf GitHub und für jeden Branch über CI ausführen:
Wichtiger Hinweis: Die oben genannten Tests werden ohne Mocks ausgeführt. Alle Backend-Dienste stehen für diese Tests zur Verfügung.
Falle A: Ich habe oft Full-Stack-„Smoke-Tests“ gesehen, die für Phantom- oder Selenium-Wrapper geschrieben wurden und eine lange Kette von Befehlen nacheinander ausführen, wobei jeder Befehl auf dem vorherigen aufbaut: Sie melden sich an und durchlaufen dann den gesamten Funktionsumfang einer Anwendung, um sicherzustellen, dass alles funktioniert. Jedes Mal, wenn man etwas ändern oder hinzufügen muss, führt dies zu Fehlern in den nachfolgenden Schritten. Nein, danke.
Falle B: Ebenso habe ich schon erlebt, dass Entwickler eine Art Makros erstellen, die gängige Aktionen ausführen, und diese Aktionen werden dann im Browser wiederholt ausgeführt, um bestimmte Zustände zu erzeugen. Das ist zwar effektiv, aber langsam – insbesondere, wenn diese Schritte umfangreiche Interaktionen mit der Benutzeroberfläche erfordern.
Dieses Mal haben wir beide Probleme vermieden, indem wir API-Hilfsprogramme genutzt haben, um die Tests genau nach unseren Anforderungen einzurichten:
In diesen ersten „Promises“ stecken ein paar nützliche Tricks. „helpers.createTeam“ nutzt unsere API, um ein neues Team und einen neuen Benutzer anzulegen (mit Dank an faker.js), und verwendet anschließend die generierten Anmeldedaten, um sich bei Nightwatch anzumelden…
…und anschließend separat ein API-Token abrufen, das es für nachfolgende Anfragen verwenden kann:
Die einfache Funktion `assign()` speichert die zurückgegebenen Objekte für die spätere Verwendung ab.
Das bedeutet, dass wir für jeden einzelnen Testblock ein neues Team und einen neuen Benutzer anlegen sowie alle Datenobjekte, die wir für die Tests benötigen. Das erscheint zwar aufwendig, aber dafür verfügen wir nun über automatisierte Tests, bei denen wir sicher sein können, dass sie voneinander isoliert sind … und das bedeutet auch, dass sie parallel ausgeführt werden können. Mehr dazu später.
Anpassung der Nightwatch-Page-Objekte an React Router
Nightwatch-Seitenobjekte setzen eine Idee um, die Martin Fowler hier vorgeschlagen hat: Man kann den Code, der in eine bestimmte Seite eingebunden wird, von den Tests trennen, die deren Funktionalität überprüfen.
Wir haben festgestellt, dass dies für die Wiederverwendung von Testcode von enormer Bedeutung ist, und es hilft uns, logisch über die von uns entwickelten Funktionen nachzudenken, insbesondere bei nicht trivialen Funktionen.
Der Vorteil wird besonders deutlich, wenn man einen umfangreicheren Pull-Request durchsieht. Auf einen Blick ist ganz klar erkennbar, worum es geht:
Anschließend können Sie sich das Seitenobjekt ansehen, um einen Eindruck davon zu bekommen, wie wir die Benutzeroberfläche nutzen, um die Anforderungen der Tests zu erfüllen:
Möglicherweise fällt Ihnen auf, dass zum Archivieren einer Vorlage die Bestätigung eines SweetAlert-Dialogfelds erforderlich ist. Wir haben Konzepte wie Modals und SweetAlert als eigene Nightwatch-Seitenobjekte erfasst, sodass die Berücksichtigung dieses Schritts kein Problem darstellt. Da dies bereits in den Befehl des Seitenobjekts integriert ist, muss der Test nicht wissen, dass eine Bestätigung erforderlich ist.
Sobald man die Tests und Seitenobjekte durchgesehen hat, ist es nur noch ein kleiner Schritt, sich die React-Komponenten anzusehen, die das Seitenobjekt implementieren, sowie die Redux-/Thrift-Infrastruktur, die die Anforderungen der React-Komponenten abwickelt. Wenn Ihnen schon einmal beim Versuch, einen umfangreichen Pull-Request zu durchforsten, die Augen trüb geworden sind, glauben Sie mir: Das ist ein unglaublicher Fortschritt.
Da React Router uns einen so übersichtlichen Einblick in unsere Sitemap bietet, können wir sicherstellen, dass die Seitenobjekte bei beiden Apps mit unseren Routen synchron bleiben – und schon kann es losgehen.
Erweitern Sie Nightwatch mit leistungsstarken Tools wie Drag-and-Drop
NightwatchJS ist ein Selenium-Wrapper, der die meisten der von uns benötigten Funktionen zur Navigation in der Benutzeroberfläche bereits standardmäßig bereitstellt. Außerdem bietet er die Möglichkeit, Skripte einzubinden, um Ihre Testmöglichkeiten zu erweitern.
In unserer App nutzen wir ausgiebig diegroßartige React-DnD-Bibliothekvon Dan Abramov. Um sie zu testen, hat Aaron einen Nightwatch-Befehl eingerichtet, der „Drag Mock“ von Andy Wermke wie folgt einbindet:
Dadurch können wir Drag-Aktionen auf Seitenobjekte anwenden:
Damit haben wir eine einfache und semantische Möglichkeit, Drag-Verhalten in Tests zu definieren:
So wie die Verwendung von JSX unseren Designern die Möglichkeit eröffnet hat, mehr Design-PRs einzureichen, ermöglichen uns diese Testtechniken, auch relativ unerfahrene QA-Automatisierungsingenieure einzubeziehen, die uns beim Schreiben hochwertiger Tests unterstützen, und manuelle QA-Tester im Team können fehlerhafte Interpretationen von Anforderungen bereits früh im Prozess erkennen. So profitieren alle davon.
Die Rolle von Full-Stack-Integrationstests
Allerdings macht keine dieser Techniken solide Einheiten überflüssig. Wir nutzen Mocha, um Redux-Reducer, -Actions usw. zu testen, und Enzyme, um React-Komponenten isoliert zu testen. Außerdem werten wir einen Testlauf als fehlgeschlagen, wenn der Quellcode die ESLint-Prüfung nicht besteht.
Ich messe diesen Full-Stack-Tests jedoch weitaus mehr Bedeutung bei, denn sie bestätigen uns: Wenn man die Mocks entfernt und alle Backend-Dienste einbindet, verfügen wir immer noch über die gesamte Funktionalität, die wir unseren Nutzern versprechen – und zwar nicht nur in Chrome auf einem Cinema Display in Soma, sondern auch unter realen Bedingungen auf einem kaputten Vista-Laptop mit IE11 auf einer Bohrinsel. Genau dort muss unsere Software überzeugen, und Nightwatch gibt uns die Gewissheit, dass wir dieses Ziel erreicht haben.











