Seitenobjekte können als leistungsstarke Methode zur Abstraktion (Isolierung) Ihrer Tests von der technischen Implementierung verwendet werden. Es ist wichtig zu bedenken, dass sie (Seitenobjekte) verwendet werden können, um die Stabilität von Tests zu erhöhen und das Prinzip von
DRY beizubehalten (wiederholen Sie sich nicht) - indem Sie die Funktionalität (Website) in einfachen Methoden einkapseln.
Mit anderen Worten
Das Seitenobjekt ist eine Instanz einer Klasse, die die Benutzeroberfläche von der Testumgebung abstrahiert (isoliert), Methoden für die Interaktion mit der Benutzeroberfläche vorstellt und die erforderlichen Informationen extrahiert.
Terminologie
Der Begriff
Seitenobjekt ist ein zu allgemeines Konzept. Nach meiner Erfahrung umfasst
Page Object die folgenden 3 Typen:
- Komponentenobjekte repräsentieren bestimmte Komponenten oder Widgets in der Benutzeroberfläche. Zum Beispiel: Tabellen, Menüs, Artikel und andere Blöcke, die eine Gruppe von Komponenten enthalten.
- Das Seitenobjekt beschreibt einen bestimmten Bereich oder eine bestimmte Benutzeroberfläche in einer Webanwendung. Es kann aus mehreren Komponentenobjekten bestehen und praktische Methoden für die Interaktion mit der in diesem Objekt enthaltenen Abstraktion enthalten.
- Erfahrung wird verwendet, um komplexe Funktionen zu gruppieren, deren Testen mehrere Schritte oder die Interaktion mit mehreren Seiten erfordert. Aus eigener Erfahrung habe ich dieses Konzept verwendet, um komplexes Verhalten auf einer Seite zu abstrahieren (Testen von Lernseiten, Erstellen eines neuen Benutzers usw.)
Beispiele
Betrachten Sie den einfachen
RSpec Capybara- Test, der Blogs erstellt und keine
Seitenobjekte verwendet:
require 'feature_helper' feature 'Blog management', type: :feature do scenario 'Successfully creating a new blog' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') fill_in 'blog_title', with: 'My Blog Title' fill_in 'blog_text', with: 'My new blog text' click_on 'Save Blog' expect(page).to have_selector('.blog--show') expect(page).to have_content('My Blog Title') expect(page).to have_content('My new blog text') end scenario 'Entering no data' do visit '/' click_on 'Form Examples' expect(page).to have_content('Create Blog') click_on 'Save Blog' expect(page).to have_content('4 errors stopped this form being submitted') expect(page).to have_content("Title can't be blank") expect(page).to have_content("Text can't be blank") expect(page).to have_content('Title is too short') expect(page).to have_content('Text is too short') end end
Schauen wir uns den Code genauer an, er hat mehrere Probleme. Es gibt folgende Aktionen: Wechseln zur entsprechenden Seite, Interaktion mit der Seite und Überprüfen des Inhalts. Ein Teil des Codes ist dupliziert, dies kann jedoch durch Einhaltung des
DRY- Prinzips behoben werden.
Es ist wichtig zu verstehen, dass dieser Code schwer zu pflegen ist, wenn sich Änderungen in der zu testenden Anwendung ergeben. Beispielsweise können sich Elementklassen, Namen und Bezeichner ändern, was regelmäßige Aktualisierungen des Testcodes erfordert.
Auch in diesem Code gibt es keinen 'semantischen Kontext', es ist schwer zu verstehen, welche Codezeilen logisch gruppiert sind.
Einführung in Seitenobjekte
Wie im Abschnitt zur Terminologie erläutert, können Seitenobjekte zur Darstellung von Abstraktionen auf Präsentationsebene verwendet werden.
Wenn Sie das vorherige Beispiel verwenden und mit
Page Object neue Blogs erstellen und Blogs anzeigen, können Sie den Code des vorherigen Beispiels löschen.
Nachdem bestimmte Informationen zur technischen Implementierung entfernt wurden, sollte das Endergebnis (Code) lesbar sein und keine spezifischen Informationen zur Benutzeroberfläche (ID, CSS-Klassen usw.) enthalten.
require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new } before :each do new_blog_page.visit_location end scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end scenario 'Entering no data' do new_blog_page.create title: '', text: '' expect(view_blog_page).to_not have_loaded expect(new_blog_page).to have_errors "Title can't be blank", "Text can't be blank", "Title is too short", "Text is too short" end end
Seitenobjekte erstellenDer erste Schritt beim Erstellen von
Seitenobjekten besteht darin, eine
grundlegende Seitenklassenstruktur zu erstellen:
module Pages class NewBlog include RSpec::Matchers include Capybara::DSL
Verbinden (Aktivieren) von
Capybara :: DSL, damit Instanzen von Seitenobjekten die in
Capybara verfügbaren Methoden verwenden können
has_css? '.foo' has_content? 'hello world' find('.foo').click
Außerdem habe ich verwendet
RSpec :: Matchers einschließen
In den obigen Beispielen wird die
Erwartungs- RSpec-Bibliothek verwendet.
Verstoßen Sie nicht gegen die Vereinbarung.
Seitenobjekte sollten keine
Erwartungen (Erwartungen) enthalten . Gegebenenfalls bevorzuge ich diesen Ansatz jedoch, um mich auf die in
Capybara integrierten Mechanismen für die Handhabung von Bedingungen zu stützen.
Der folgende
Capybara- Code
erwartet beispielsweise das Vorhandensein von
'foo' in
Seitenobjekten (in diesem Fall
self ):
expect(self).to have_content 'foo'
Im folgenden Code jedoch:
expect(page_object.content).to match 'foo'
Unerwartete Fehler sind möglich (möglicherweise tritt ein schwebender Test auf), da
page_object.content sofort auf Übereinstimmung mit der Bedingung überprüft wird und möglicherweise noch nicht deklariert wurde. Für weitere Beispiele würde ich empfehlen,
die zuverlässigen asynchronen Integrationstests von Thoughtbot mit Capybara zu lesen.
Methodenerstellung
Wir können den Ort (die Region), von dem wir Daten erhalten möchten, im Rahmen einer Methode abstrahieren (beschreiben):
def visit_location visit '/blogs/new'
Es ist wichtig, die
semantisch korrekten Namen für die Methoden für Ihre
Seitenobjekte auszuwählen
def create(title:, text:)
Im Allgemeinen ist es wichtig, das Prinzip der funktional integrierten Methoden zu befolgen und nach Möglichkeit das Prinzip der Einzelverantwortung (Prinzip der Einzelverantwortung) einzuhalten.
Komponentenobjekte
In unserem Beispiel verwenden wir die NewBlog-Klasse, es muss jedoch keine Implementierung erstellt werden.
Da wir mit dem Formular interagieren, können wir zusätzlich eine Klasse einführen, um diese Komponente darzustellen:
Wo die Implementierung von Methoden für BlogForm ausgeblendet werden kann:
module Components class BlogForm include RSpec::Matchers include Capybara::DSL def create(title:, text:) within blog_form do fill_in 'blog_title', with: title fill_in 'blog_text', with: text click_on 'Save Blog' end end private def blog_form find('.blog--new') end end end
Alle zusammen
Mit den oben genannten Klassen können Sie jetzt die
Seitenobjekte Ihrer Seite als Teil der Beschreibung des Objekts abfragen und instanziieren.
require 'feature_helper' require_relative '../pages/new_blog' require_relative '../pages/view_blog' feature 'Blog management', type: :feature do let(:new_blog_page) { ::Pages::NewBlog.new } let(:view_blog_page) { ::Pages::ViewBlog.new }
Hinweis: Ich habe absichtlich ein Seitenobjekt manuell oben in der Objektdatei erstellt. In einigen RSpec-Tests kann es praktisch sein, alle Unterstützungsdateien automatisch herunterzuladen und in Objektdateien darauf zuzugreifen. Dies kann jedoch zu übermäßiger Arbeitsbelastung führen, wenn große Codeteile verwendet werden. Dies führt insbesondere zu einem langsamen Start und möglichen unbeabsichtigten zyklischen Abhängigkeiten.
Seitenobjekte aufrufen
Jetzt haben wir in jedem Szenario Zugriff auf Instanzen von
new_blog_page und
view_blog_page :
scenario 'Successfully creating a new blog' do new_blog_page.create title: 'My Blog Title', text: 'My new blog text' expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text' end
Namenskonventionen / Prädikatmethoden
Wie bei den meisten Dingen in Rails / Ruby gibt es Konventionen, die auf den ersten Blick unbedeutend (nicht bindend) erscheinen.
In unseren Tests haben wir mit
have_loaded und
have_blog mit dem
Seitenobjekt interagiert :
expect(view_blog_page).to have_loaded expect(view_blog_page).to have_blog title: 'My Blog Title', text: 'My new blog text'
Die Methodennamen unseres
Seitenobjekts wurden jedoch tatsächlich
geladen. und
has_blog? ::
def has_loaded?
Dies ist eine subtile Unterscheidung, die Aufmerksamkeit erfordert. Für weitere Informationen zu dieser Konvention würde ich empfehlen, den folgenden Link zu
Prädikat-Matchern zu lesen.
Git, der in den Beispielen verwendete QuellcodeDas Original