Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby
Code

Ruby Page Objekte für Capybara-Kenner

by
Length:LongLanguages:

German (Deutsch) translation by Katharina Nevolina (you can also view the original English article)

Final product image
What You'll Be Creating

Was sind Seitenobjekte?

Ich gebe Ihnen zuerst die kurze Tonhöhe. Es handelt sich um ein Entwurfsmuster zum Einkapseln von Markup- und Seiteninteraktionen, insbesondere zum Umgestalten Ihrer Feature-Spezifikationen. Es handelt sich um eine Kombination aus zwei sehr verbreiteten Refactoring-Techniken: Klasse extrahieren und Methode extrahieren. Das muss nicht gleichzeitig geschehen, da Sie schrittweise eine ganze Klasse über ein neues Seitenobjekt extrahieren können.

Mit dieser Technik können Sie übergeordnete Funktionsspezifikationen schreiben, die sehr ausdrucksstark und trocken sind. In gewisser Weise handelt es sich um Abnahmetests mit Anwendungssprache. Sie fragen sich vielleicht, sind die mit Capybara geschriebenen Spezifikationen nicht bereits auf hohem Niveau und ausdrucksstark? Sicher, für Entwickler, die täglich Code schreiben, lesen sich die Capybara-Spezifikationen einwandfrei. Sind sie sofort trocken? Nicht wirklich - eigentlich schon gar nicht!

Wenn Sie sich diese Beispiele für Funktionsspezifikationen ansehen, wo sehen Sie Möglichkeiten, diese besser zu lesen, und wie können Sie Informationen extrahieren, um Doppelarbeit zu vermeiden? Reicht dieses Niveau auch aus, um User Stories einfach zu modellieren und nicht-technische Stakeholder zu verstehen?

Meiner Meinung nach gibt es einige Möglichkeiten, dies zu verbessern und alle glücklich zu machen - Entwickler, die es vermeiden können, bei der Anwendung von OOP mit den Details der Interaktion mit dem DOM herumzuspielen, und andere nicht codierende Teammitglieder, die keine Probleme haben, zwischen User Stories zu wechseln und diese Tests. Der letzte Punkt ist sicher schön zu haben, aber die wichtigsten Vorteile ergeben sich hauptsächlich aus der Robustheit Ihrer DOM-interagierenden Spezifikationen.

Kapselung ist das Schlüsselkonzept bei Seitenobjekten. Wenn Sie Ihre Funktionsspezifikationen schreiben, profitieren Sie von einer Strategie zum Extrahieren des Verhaltens, das einen Testablauf durchläuft. Für Qualitätscode möchten Sie die Interaktionen mit bestimmten Elementgruppen auf Ihren Seiten erfassen, insbesondere wenn Sie auf sich wiederholende Muster stoßen. Wenn Ihre Anwendung wächst, möchten/benötigen Sie einen Ansatz, der verhindert, dass diese Logik über Ihre Spezifikationen verteilt wird.

"Nun, ist das nicht übertrieben? Capybara liest sich gut! “ sagen Sie?

Fragen Sie sich: Warum haben Sie nicht alle Details der HTML-Implementierung an einem Ort, während Sie stabilere Tests durchführen? Warum sollten UI-Interaktionstests nicht die gleiche Qualität haben wie Tests für Anwendungscode? Wollen Sie wirklich dort aufhören?

Aufgrund alltäglicher Änderungen ist Ihr Capybara-Code anfällig, wenn er überall verteilt ist - er führt mögliche Haltepunkte ein. Angenommen, ein Designer möchte den Text auf einer Schaltfläche ändern. Kein Problem, oder? Aber möchten Sie sich an diese Änderung in einem zentralen Wrapper für dieses Element in Ihren Spezifikationen anpassen, oder möchten Sie dies lieber überall tun? Ich dachte auch!

Es sind viele Refactorings für Ihre Feature-Spezifikationen möglich, aber Seitenobjekte bieten die saubersten Abstraktionen für die Kapselung des Benutzerverhaltens für Seiten oder komplexere Abläufe. Sie müssen jedoch nicht die gesamte (n) Seite (n) simulieren - konzentrieren Sie sich auf die wesentlichen Elemente, die für den Benutzerfluss erforderlich sind. Keine Notwendigkeit, es zu übertreiben!

Abnahmetests / Funktionsspezifikationen

Bevor wir zum Kern der Sache übergehen, möchte ich für Leute, die neu im gesamten Testgeschäft sind, einen Schritt zurücktreten und einen Teil der in diesem Zusammenhang wichtigen Umgangssprache klären. Menschen, die mit TDD besser vertraut sind, werden nicht viel verpassen, wenn sie weitermachen.

Worüber reden wir hier? Akzeptanztests werden normalerweise zu einem späteren Zeitpunkt in Projekten durchgeführt, um zu bewerten, ob Sie etwas Wertvolles für Ihre Benutzer, Produktbesitzer oder andere Stakeholder aufgebaut haben. Diese Tests werden normalerweise von Kunden oder Ihren Benutzern durchgeführt. Es ist eine Art Überprüfung, ob die Anforderungen erfüllt werden oder nicht. Es gibt so etwas wie eine Pyramide für alle Arten von Testschichten, und Akzeptanztests befinden sich ganz oben. Da dieser Prozess häufig nicht-technische Personen umfasst, ist eine Hochsprache zum Schreiben dieser Tests ein wertvolles Gut für die Kommunikation zwischen den beiden.

Die Funktionsspezifikationen sind dagegen in der Testnahrungsmittelkette etwas niedriger. Viel mehr als Unit-Tests, die sich auf die technischen Details und die Geschäftslogik Ihrer Modelle konzentrieren, beschreiben Feature-Spezifikationen die Abläufe auf und zwischen Ihren Seiten.

Werkzeuge wie Capybara helfen Ihnen dabei, dies nicht manuell zu tun, sodass Sie Ihren Browser selten öffnen müssen, um Dinge manuell zu testen. Mit solchen Tests möchten wir diese Aufgaben so weit wie möglich automatisieren und die Interaktion über den Browser testen, während wir Aussagen gegen Seiten schreiben. Übrigens verwenden Sie get, put, post oder delete nicht wie bei Anforderungsspezifikationen.

Die Funktionsspezifikationen sind den Abnahmetests sehr ähnlich. Manchmal sind die Unterschiede meiner Meinung nach zu verschwommen, um sich wirklich um die Terminologie zu kümmern. Sie schreiben Tests, die Ihre gesamte Anwendung ausführen, was häufig einen mehrstufigen Ablauf von Benutzeraktionen umfasst. Diese Interaktionstests zeigen, ob Ihre Komponenten beim Zusammenführen harmonisch funktionieren.

In Ruby Land sind sie der Hauptprotagonist, wenn es um Seitenobjekte geht. Die Feature-Spezifikationen selbst sind bereits sehr aussagekräftig, können jedoch optimiert und bereinigt werden, indem ihre Daten, ihr Verhalten und ihr Markup in eine oder mehrere separate Klassen extrahiert werden.

Ich hoffe, dass Sie durch das Aufklären dieser verschwommenen Terminologie erkennen können, dass das Erstellen von Seitenobjekten ein bisschen wie das Durchführen von Tests auf Akzeptanzebene beim Schreiben von Funktionsspezifikationen ist.

Capybara

Vielleicht sollten wir das auch sehr schnell durchgehen. Diese Bibliothek beschreibt sich selbst als "Akzeptanztest-Framework für Webanwendungen". Sie können Benutzerinteraktionen mit Ihren Seiten über eine sehr leistungsstarke und praktische domänenspezifische Sprache simulieren. Meiner persönlichen Meinung nach bietet RSpec in Kombination mit Capybara derzeit die beste Möglichkeit, Ihre Funktionsspezifikationen zu schreiben. Sie können Seiten besuchen, Formulare ausfüllen, auf Links und Schaltflächen klicken und auf Ihren Seiten nach Markups suchen. Sie können alle Arten dieser Befehle kombinieren, um durch Ihre Tests mit Ihren Seiten zu interagieren.

Grundsätzlich können Sie vermeiden, den Browser selbst zu öffnen, um dieses Material die meiste Zeit manuell zu testen - was nicht nur weniger elegant, sondern auch viel zeitaufwändiger und fehleranfälliger ist. Ohne dieses Werkzeug wäre der Prozess des „Outside-In-Testings“ - Sie steuern Ihren Code von Tests auf hoher Ebene bis hin zu Tests auf Einheitenebene - viel schmerzhafter und wird daher möglicherweise vernachlässigt.

Mit anderen Worten, Sie beginnen mit dem Schreiben dieser Feature-Tests, die auf Ihren User Stories basieren, und von dort aus gehen Sie das Kaninchenloch hinunter, bis Ihre Unit-Tests die Abdeckung bieten, die Ihre Feature-Spezifikationen benötigen. Danach, wenn Ihre Tests natürlich grün sind, beginnt das Spiel von neuem und Sie gehen zurück, um mit einem neuen Funktionstest fortzufahren.

Wie?

Schauen wir uns zwei einfache Beispiele für Feature-Spezifikationen an, mit denen M klassifizierte Missionen erstellen kann, die dann abgeschlossen werden können.

Im Markup haben Sie eine Liste von Missionen, und ein erfolgreicher Abschluss erstellt eine zusätzliche Klasse completed, die im Rahmen dieser Mission auf li abgeschlossen wurde. Einfaches Zeug, richtig? Als ersten Ansatz begann ich mit kleinen, sehr häufigen Refactorings, die gemeinsames Verhalten in Methoden extrahieren.

spec/features/m_creates_a_mission_spec.rb

spec/features/agent_completes_a_mission_spec.rb

Obwohl es natürlich auch andere Möglichkeiten gibt, mit Dingen wie sign_in_as, create_classified_mission_named usw. umzugehen, ist es leicht zu erkennen, wie schnell diese Dinge anfangen können zu saugen und sich zu häufen.

UI-bezogene Spezifikationen erhalten oft nicht die OO-Behandlung, die sie benötigen / verdienen, denke ich. Sie haben den Ruf, zu wenig Geld zu verdienen, und natürlich mögen Entwickler Zeiten, in denen sie Markup-Sachen viel anfassen müssen, nicht besonders. In meinen Augen ist es deshalb noch wichtiger, diese Spezifikationen zu trocknen und es Spaß zu machen, mit ihnen umzugehen, indem Sie ein paar Ruby-Klassen absolvieren.

Lassen Sie uns einen kleinen Zaubertrick machen, bei dem ich die Implementierung des Seitenobjekts vorerst verstecke und Ihnen nur das Endergebnis zeige, das auf die obigen Funktionsspezifikationen angewendet wurde:

spec/features/m_creates_a_mission_spec.rb

spec/features/agent_completes_a_mission_spec.rb

Liest nicht schlecht, oder? Grundsätzlich erstellen Sie auf Ihren Seitenobjekten ausdrucksstarke Wrapper-Methoden, mit denen Sie sich mit Konzepten auf hoher Ebene befassen können - anstatt ständig überall mit dem Darm Ihres Markups herumzuspielen. Ihre extrahierten Methoden erledigen diese Art von Drecksarbeit jetzt, und auf diese Weise ist eine Schrotflintenoperation nicht mehr Ihr Problem.

Ihre extrahierten Methoden erledigen diese Art von Drecksarbeit jetzt, und auf diese Weise ist eine Schrotflintenoperation nicht mehr Ihr Problem. Ich muss jedoch sagen, dass manchmal intelligent extrahierte Methoden in Ihren Feature-Spezifikationen ausreichen und etwas besser lesen, da Sie den Umgang mit Seitenobjektinstanzen vermeiden können. Werfen wir einen Blick auf die Implementierung:

specs/support/features/pages/missions.rb

Was Sie sehen, ist ein einfaches altes Ruby-Objekt - Seitenobjekte sind im Wesentlichen sehr einfache Klassen. Normalerweise instanziieren Sie Seitenobjekte nicht mit Daten (wenn dies erforderlich ist, können Sie dies natürlich) und erstellen meistens eine Sprache über die API, die ein Benutzer oder ein nicht technischer Stakeholder in einem Team möglicherweise verwendet. Wenn Sie über die Benennung Ihrer Methoden nachdenken, ist es meiner Meinung nach ein guter Rat, sich die Frage zu stellen: Wie würde ein Benutzer den Ablauf oder die ergriffenen Maßnahmen beschreiben?

Ich sollte vielleicht hinzufügen, dass die Musik ohne Capybara ziemlich schnell aufhört.

Sie fragen sich wahrscheinlich, wie diese benutzerdefinierten Matcher funktionieren:

RSpec generiert diese benutzerdefinierten Matcher basierend auf Prädikatmethoden für Ihre Seitenobjekte. RSpec konvertiert sie durch Entfernen des ? und ändert has auf have. Boom, Matcher von Grund auf ohne viel Flaum! Ein bisschen Magie, das gebe ich dir, aber die gute Art von Zauberei, würde ich sagen.

Da wir unser Seitenobjekt unter specs/support/feature/pages/missions.rb geparkt haben, müssen Sie auch sicherstellen, dass Folgendes in spec/rails_helper.rb nicht auskommentiert ist.

Wenn Sie auf einen NameError mit uninitialized constant Pages stoßen, wissen Sie, was zu tun ist.

Wenn Sie neugierig sind, was mit der Methode sign_in_as passiert ist, habe ich sie in ein Modul unter spec/support/sign_in_helper.rb extrahiert und RSpec angewiesen, dieses Modul einzuschließen. Das hat nichts direkt mit Seitenobjekten zu tun. Es ist nur sinnvoller, Testfunktionen wie sign in die Anmeldung global zugänglicher zu speichern als über ein Seitenobjekt.

spec/support/sign_in_helper.rb

Und Sie müssen RSpec mitteilen, dass Sie auf dieses Hilfsmodul zugreifen möchten:

spec/spec_helper.rb

Insgesamt ist leicht zu erkennen, dass es uns gelungen ist, die Capybara-Besonderheiten zu verbergen - wie das Finden von Elementen, das Klicken auf Links usw. Wir können uns jetzt auf die Funktionalität und weniger auf die tatsächliche Struktur des Markups konzentrieren, das jetzt in einem Seitenobjekt gekapselt ist - Die DOM-Struktur sollte das geringste Problem für Sie sein, wenn Sie etwas testen, das so hoch ist wie die Funktionsspezifikationen.

Beachtung!

Einrichtungsgegenstände wie Werksdaten gehören in die Spezifikationen und nicht in Seitenobjekte. Außerdem werden Behauptungen wahrscheinlich besser außerhalb Ihrer Seitenobjekte platziert, um eine Trennung der Bedenken zu erreichen.

Es gibt zwei verschiedene Perspektiven zu diesem Thema. Befürworter des Einfügens von Behauptungen in Seitenobjekte sagen, dass dies dazu beiträgt, doppelte Behauptungen zu vermeiden. Sie können bessere Fehlermeldungen bereitstellen und einen besseren "Tell, Don't Ask"-Stil erzielen. Befürworter von durchsetzungsfreien Seitenobjekten argumentieren jedoch, dass es besser ist, Verantwortlichkeiten nicht zu vermischen. Das Bereitstellen des Zugriffs auf Seitendaten und die Bestätigungslogik sind zwei separate Probleme und führen beim Mischen zu aufgeblähten Seitenobjekten. Die Verantwortung von Page Object liegt im Zugriff auf den Status der Seiten, und die Assertionslogik gehört zu den Spezifikationen.

Seitenobjekttypen

Komponenten stellen die kleinsten Einheiten dar und sind fokussierter - wie beispielsweise ein Formularobjekt.

Seiten kombinieren mehr dieser Komponenten und sind Abstraktionen einer ganzen Seite.

Wie Sie bereits vermutet haben, erstrecken sich die Erfahrungen über den gesamten Fluss auf potenziell viele verschiedene Seiten. Sie sind hochrangiger. Sie konzentrieren sich auf den Fluss, den der Benutzer erfährt, während er mit verschiedenen Seiten interagiert. Ein Checkout-Ablauf mit mehreren Schritten ist ein gutes Beispiel, um darüber nachzudenken.

Wann warum?

Es ist eine gute Idee, dieses Entwurfsmuster etwas später im Lebenszyklus eines Projekts anzuwenden - wenn Sie ein wenig Komplexität in Ihren Feature-Spezifikationen angehäuft haben und wenn Sie sich wiederholende Muster wie DOM-Strukturen, extrahierte Methoden oder andere Gemeinsamkeiten identifizieren können konsistent auf Ihren Seiten.

Sie sollten also wahrscheinlich nicht sofort mit dem Schreiben von Seitenobjekten beginnen. Sie nähern sich diesen Refactorings schrittweise, wenn die Komplexität und Größe Ihrer Anwendung / Tests zunimmt. Duplikate und Refactorings, die ein besseres Zuhause durch Seitenobjekte benötigen, sind im Laufe der Zeit leichter zu erkennen.

Meine Empfehlung ist, mit dem lokalen Extrahieren von Methoden in Ihren Feature-Spezifikationen zu beginnen. Sobald sie die kritische Masse erreicht haben, sehen sie wie offensichtliche Kandidaten für eine weitere Extraktion aus, und die meisten von ihnen passen wahrscheinlich zum Profil für Seitenobjekte. Fangen Sie klein an, denn vorzeitige Optimierung hinterlässt böse Bissspuren!

Abschließende Gedanken

Seitenobjekte bieten Ihnen die Möglichkeit, klarere Spezifikationen zu schreiben, die besser lesen und insgesamt viel aussagekräftiger sind, weil sie ein höheres Niveau aufweisen. Außerdem bieten sie eine schöne Abstraktion für alle, die gerne OO-Code schreiben. Sie verbergen die Besonderheiten des DOM und ermöglichen Ihnen auch private Methoden, die die Drecksarbeit erledigen, während Sie der öffentlichen API nicht ausgesetzt sind. Extrahierte Methoden in Ihren Funktionsspezifikationen bieten nicht den gleichen Luxus. Die API von Seitenobjekten muss die Details von Capybara nicht freigeben.

In allen Szenarien, in denen sich Designimplementierungen ändern, müssen sich Ihre Beschreibungen der Funktionsweise Ihrer App nicht ändern, wenn Sie Seitenobjekte verwenden. Ihre Funktionsspezifikationen konzentrieren sich mehr auf Interaktionen auf Benutzerebene und kümmern sich nicht so sehr um die Besonderheiten der DOM-Implementierungen. Da Änderungen unvermeidlich sind, werden Seitenobjekte kritisch, wenn Anwendungen wachsen, und helfen auch zu verstehen, wenn die schiere Größe der Anwendung eine drastisch erhöhte Komplexität bedeutet.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.