Cucumber: Web-Anwendungen mit Capybara, Poltergeist und PhantomJS testen

Keine Kommentare

Dieses Tutorial zeigt, wie sich Akzeptanztests für Web-Anwendungen mit Cucumber, Capybara, Poltergeist und PhantomJS schreiben lassen. Unterwegs werden wir auch einige andere interessante Technologien wie Node.js and AngularJS streifen. Dies ist der zweite Teil einer Mini-Serie zu Cucumber – wem Cucumber bisher noch nicht bekannt ist, kann auch mit dem ersten Teil einsteigen, der das Setup und die Grundlagen von Cucumber behandelt.

Der Tool-Stack im Detail – Capybara, Poltergeist und PhantomJS

Bevor wir unseren ersten Test schreiben, sollten wir uns einen Moment Zeit nehmen, um die Werkzeuge zu betrachten, die wir zusätzlich zu Cucumber noch einsetzen werden:

  • Capybara nennt sich selbst ein „Akzeptanztest-Framework für Web-Anwendungen“. Moment mal – ist Cucumber nicht schon ein Akzeptanztest-Framework? Warum benötigen wir noch ein weiteres in unserem Stack? Zuerst einmal ist Cucumber ein Framework für Behaviour Driven Development und nicht per se ein Akzeptanztest-Framework. Man könnte Cucumber für Unit Tests einsetzen, für Akzeptanztests oder für irgendetwas dazwischen. Des Weiteren hat Cucumber nichts mit Web-Anwendungen am Hut. Cucumber gibt uns nur die Möglichkeit, unsere Tests (oder Specs oder Szenarios, wie immer man sie auch nennen möchte) in einer sehr lesbaren, nicht-technischen Form (Gherkin) zu schreiben und auszuführen. Capybara hingegen kümmert sich nun um den Aspekt „Web-Anwendung“ und versteckt die Details, die notwendig sind, um einen Browser fernzusteuern (via Selenium oder Poltergeist) hinter einer schicken API. Des Weiteren lässt sich Capybara gut mit Cucumber kombinieren (siehe Abschnitt Using Capybara with Cucumber in der Capybara-Dokumentation). Es ist genau der richtige Abstraktionslevel, um Tests für Web-Anwendungen zu schreiben. Natürlich könnte man auch in den Cucumber Step Definitions so etwas wie Selenium direkt verwenden, aber meiner Meinung nach ist das zu low-level und erfordert deutlich mehr Code für das gleiche Ergebnis.
  • Poltergeist ist ein PhantomJS Treiber für Capybara. Es stellt die Verbindung zwischen Capybara und PhantomJS dar. Nachdem Poltergeist einmal als Treiber in Capybara konfiguriert ist, interagiert man nicht mehr direkt mit Poltergeist oder PhantomJS, sondern nur noch mit der Capybara API.
  • PhantomJS ist ein vollständiger Browser, der allerdings headless (also ohne graphische Anzeige) läuft. Es basiert auf WebKit und unterstützt alle wichtigen Web-Standards (CSS, JavaScript, …). Es eignet sich ideal für automatisierte Tests von Web-Anwendungen, da es ausgesprochen einfach ist, es auf einem CI-Server laufen zu lassen (selbst, wenn auf diesem kein X installiert ist).

Setup

Einrichten der zu testenden Anwendung

Da dieses Tutorial sich mit dem Testen von Web-Anwendungen beschäftigt, benötigen wir offensichtlich eine solche. Wer öfter Beiträge im codecentric-Blog liest, dem ist vielleicht schon einmal die „Movie Database“ in einer Ihrer verschiedenen Inkarnationen begegnet. Da ich mich kaum für Filme interessiere, dafür aber umso mehr für Hörbücher, wird unsere Beispielanwendung diesmal der „Audiobook Collection Manager“ sein, eine sehr einfache, auf AngularJS basierende CRUD-Anwendung, mit der man seine Hörbuch-Sammlung verwalten kann. Unter https://github.com/basti1302/audiobook-collection-manager-ui findet sich eine kurze Anleitung, um die Anwendung einzurichten. Im Verlauf der Installation muss auch Storra per git geklont werden, ein simpler, in Node.js geschriebender REST-Service, der vom Audiobook Collection Manager Frontend zur Speicherung der Daten benutzt wird. Kleiner Hinweis am Rande: Im ersten Teil des Tutorials hatte ich erwähnt, dass Cucumber mittlerweile auf viele andere Sprachen (außer Ruby) portiert wurde. Wer einen Blick auf cucumber.js, den JavaScript-Port von Cucumber, werfen möchte, sollte einmal in das Verzeichnis storra/features schauen.

Wenn der Audiobook Collection Manager eingerichtet ist und läuft, kann man kurz manuell ein wenig in der Anwendung umherklicken, um zu sehen, was man damit machen kann (nicht viel – es ist ja auch nur eine Beispielanwendung).

Capybara und Poltergeist einrichten

Wer den ersten Teil des Tutorials verfolgt hat, sollte das Beispiel-Projekt bereits ausgecheckt haben. In diesem Fall muss

git checkout 03_setup_capybara

ausgeführt werden, um den neuen Gemfile mit den Gems für Capybara und Poltergeist zu bekommen. (In diesem Branch wurde das Cucumber Feature und die dazugehörige Step-Datei aus dem ersten Teil des Tutorials entfernt, da wir es nicht mehr benötigen.)

Wenn das Repository noch nicht ausgecheckt ist, sollte das jetzt nachgeholt werden.

git clone -b 03_setup_capybara https://github.com/basti1302/audiobook-collection-manager-acceptance.git

Da sich das Gemfile geändert hat, muss nun als erstes bundle install ausgeführt werden. Damit werden die Capybara- und Poltergeist-Gems und deren Abhängigkeiten installiert.

Im ersten Teil des Tutorials wurde auch PhantomJS installiert. Wer diesen Schritt ausgelassen hat, kann das jetzt nachholen – http://phantomjs.org/download.html.

Der Branch von audiobook-collection-manager-acceptance, den wir gerade ausgecheckt haben, beinhaltet bereits den erforderlichen Code zu Konfiguration von Capybara und Poltergeist. An dieser Stelle ist also nichts mehr zu tun. Wir können uns den Konfigurations-Code kurz anschauen, er ist aufgeteilt auf features/support/env.rb und features/support/hooks.rb. Die Datei env.rb beginnt mit einigen require-Anweisungen:

require 'rspec/expectations'
require 'capybara/cucumber'
require 'capybara/poltergeist'

Die Anweisung require 'rspec/expectations' habe wir bereits im ersten Teil des Tutorials diskutiert – sie sorgt dafür, dass die RSpec Object Expectations in allen Step-Files verfügbar sind. require 'capybara/cucumber' bewirkt das gleiche für die Methoden der Capybara API. require 'capybara/poltergeist' ist erforderlich, um Poltergeist als Browser-Treiber für Capybara zu registrieren.

Nach dem require-Block kommt ein etwas längliches if-else-Konstrukt:

if ENV['IN_BROWSER']
  # On demand: non-headless tests via Selenium/WebDriver
  # To run the scenarios in browser (default: Firefox), use the following command line:
  # IN_BROWSER=true bundle exec cucumber
  # or (to have a pause of 1 second between each step):
  # IN_BROWSER=true PAUSE=1 bundle exec cucumber
  Capybara.default_driver = :selenium
  AfterStep do
    sleep (ENV['PAUSE'] || 0).to_i
  end
else
  # DEFAULT: headless tests with poltergeist/PhantomJS
  Capybara.register_driver :poltergeist do |app|
    Capybara::Poltergeist::Driver.new(
      app,
      window_size: [1280, 1024]#,
      #debug:       true
    )
  end
  Capybara.default_driver    = :poltergeist
  Capybara.javascript_driver = :poltergeist
end

Es nicht notwendig, das im Detail zu analysieren, der springende Punkt ist: Im Standardfall benutzen wir den else-Zweig. Dort wird Poltergeist als Treiber für Capybara registriert, Poltergeist wiederum benutzt dann PhantomJS. Da PhantomJS headless läuft, ist es sehr einfach in eine Continuous-Integration-Umgebung zu integrieren.

Wenn man sich das Ganze allerdings ab und an in einem richtigen Browser anschauen will, kann man die Tests folgendermaßen starten:

IN_BROWSER=true bundle exec cucumber

oder unter Windows:

SET IN_BROWSER=true
bundle exec cucumber

Wenn diese Umgebungsvariable vorhanden ist, wird Selenium WebDriver statt Poltergeist und Firefox statt PhantomJS benutzt, so dass man sich die Szenarien live und in Farbe anschauen kann. Wem das zu schnell geht, kann sie mit

IN_BROWSER=true PAUSE=1 bundle exec cucumber

oder unter Windows

SET IN_BROWSER=true
SET PAUSE=1
bundle exec cucumber

starten, so dass Cucumber nach jedem Step eine Sekunde wartet.

Der Rest von env.rb definiert einige Konstanten sowie Hilfsmethoden, um auf die getestete Anwendung zuzugreifen.

hooks.rb enhält momentan nur Folgendes:

After do |scenario|
  if scenario.failed?
    save_page
  end
end

Dies registriert einen sogenannten Hook, der nach jedem Szenario aufgerufen wird. Wenn das Szenario fehlgeschlagen ist, schreibt Capybara eine Momentaufnahme des HTMLs der aktuellen Seite in eine Datei. Cucumber documentation enthält weitergehende Informationen zu Hooks.

Testen der Web-Anwendung

Das erste Feature mit Capybara

Nachdem nun das Setup und die Konfiguration erledigt ist, können wir mit unserem ersten Szenario loslegen.

Die erste Funktion, die wir testen werden, ist das Auflisten aller Hörbücher in der Sammlung. Ohne weitere Umschweife schreiben wir einfach auf, welches Verhalten wir von der Anwendung erwarten, wenn der Benutzer auf die Seite geht, die die Liste aller Einträge darstellt. Die Datei features/list_audiobooks.feature könnte folgendermaßen aussehen:

Feature: Display the list of audio books
  In order to know which audio books the collection contains
  As an audio book enthusiast
  I want to see a list of all audio books
 
  Scenario: Display the list of all audio books in the collection
    Given some audio books in the collection
    When I visit the list of audio books
    Then I see all audio books

Anmerkung: Sämtlicher Code der in diesem Abschnitt zu sehen ist (das Cucumber-Feature, der Step-File und die Datei storage.rb) ist im Branch 04_list_audiobooks verfügbar. Mit git checkout 04_list_audiobooks kann man sich den Code holen.

Zur Erinnerung: In einem Szenario kann man im Wesentlichen schreiben, was man möchte, solange jede Zeile mit Given, When, Then oder And beginnt. Man muss (und sollte) an dieser Stelle noch nicht darüber nachdenken, wie man die einzelnen Schritte später implementiert. Stattdessen kann man sich ganz darauf konzentrieren, wie man die aktuellen Anforderung formuliert. Aus diesem Grund ist es oft günstiger, das Szenario zuerst zu schreiben und die Step Definitions erst im Anschluss. Das obige Szenario sollte weitestgehend selbsterklärend sein.

Wir können bei Bedarf nun wieder bundle exec cucmber ausführen, um von Cucumber Vorschläge für die Step Definitions zu bekommen. Die Step Definitions kommen diesmal in die Datei features/step_definitions/audiobooks.rb:

#encoding: utf-8
 
Given /^some audio books in the collection$/ do
  upload_fixtures backend_url('audiobooks'), $fixtures
end
 
When /^I visit the list of audio books$/ do
  visit ui_url '/index.html'
end
 
Then /^I see all audio books$/ do
  page.should have_content 'Coraline'
  page.should have_content 'Man In The Dark'
  page.should have_content 'Siddhartha'
end

Diese Step Definitions benutzen die Capybara DSL, um die Schritte des Szenarios zu implementieren – mehr Informationen zur Capybara DSL findet man auf der Capybara Github-Seite oder auf rubydoc.info.

Der „Given“ Step ruft upload_fixtures auf, eine Methode die in features/support/storage.rb implementiert ist und Storra (der Persistenz-Service der von der AngularJS UI benutzt wird) direkt anspricht, um einige Hörbücher in die Datenbank zu schreiben. Der Code hat nicht mit Cucumber oder Capybara zu tun, daher gehe ich an dieser Stelle nicht weiter darauf ein.

Der „When“ Step: Klammern sind in Ruby bei Methodenaufrufen optional, daher ist visit ui_url '/index.html' äquivalent zu visit(ui_url('/index.html')) (meiner Meinung ist die Version ohne Klammern flüssiger lesbar, aber das ist natürlich Geschmackssache). ui_url ist eine Hilfsmethode aus env.rb die einen Pfad in eine vollständige URL in der zu testenden Anwendung übersetzt. visit schließlich ist eine Methode von Capybara, die zu der gegebenen URL navigiert. Insgesamt veranlasst visit ui_url '/index.html' also den Browser (PhantomJS in diesem Fall) http://localhost:8000/app/index.html abzurufen, die Seite, die die Hörbücher auflistet.

Der „Then“ Step benutzt Capybaras page-Objekt (welches die aktuelle Seite repräsentiert, also den DOM, der momentan im Browser vorliegt) um zu verifizieren, dass drei bestimmte Textfragmente vorhanden sind. Nehmen wir einmal an, dass der Step Given some audio books in the collection drei Hörbücher hinzufügt, und zwar „Coraline“ von Neil Gaiman, „Man in the Dark“ von Paul Auster und „Siddhartha“ von Herman Hesse. Wenn man sich die Dokumentation von Capybara ansieht, würde man nun erwarten, dass die Überprüfung in etwa so erfolgt: page.has_content?('Coraline'). Um das ganze etwas interessanter zu machen, benutzen wir hier die Fähigkeit von RSpec, zu jedem Prädikat (eine Methode, deren Name mit „?“ endet) bei Bedarf zur Laufzeit einen Custom Matcher zu generieren. Siehe dazu auch die RSpec Matcher Doku. Die RSpec-Bibliothek nutzt hierzu Meta-Programming in Ruby und daher können wir page.should(have_content('Coraline')) schreiben oder eben ohne Klammern page.should have_content 'Coraline'.

Ein Wort zu Test-Daten

Wenn man das Feature mehrfach ausführt und dann http://localhost:8000/app/index.html im Browser öffnet, wird man feststellen, dass die Liste ziemlich gewachsen ist, sie enthält jetzt jeweils mehrere Exemplare der Hörbücher, die im „Given“ Step hinzugefügt wurden. Das kann zu einem echten Problem werden, insbesondere, wenn wir später Funktionen wir das Hinzufügen oder Löschen von Hörbüchern testen wollen. Die Tests sind nicht mehr voneinander isoliert, da sie auf derselben Datenbank operieren. Beispiel: Ein Szenario für das Hinzufügen eines neuen Hörbuchs, dass beispielsweise den Titel „Foobar“ in die Sammlung einfügt. Um zu testen, ob das Hinzufügen erfolgreich war, würden wir vermutlich page.should have_content 'Foobar' in einem „Then“ Step ausführen, nachdem wir zur Liste aller Hörbücher zurücknavigiert haben. Nun nehmen wir an, dass der Test einige Male erfolgreich durchläuft. Aber was, wenn später die Funktion zum Hinzufügen im produktiven Code kaputt geht? Unser Cucumber-Szenario wäre unter Umständen immer noch grün, da bereits mehrere Exemplare von „Foobar“ in der Datenbank sind und damit in der Liste auftauchen. Das ist der Worst Case für einen automatisierten Test: Der Test ist grün obwohl der produktive Code kaputt ist. Also müssen wir uns darum kümmen.

Dieses Problem kann bei jeder Anwendung bei Akzeptanztests auftreten und es gibt mindestens zwei Lösungen dafür:

  1. Vor oder nach jedem Test wird die komplette Datenbank gelöscht. Das ist oft die einfachere Option. Diese Strategie macht es natürlich erforderlich, im CI einen gesondertes Umgebung für die Akzeptanztests bereitzustellen – zusätzlich zu einer Stage für manuelle Tests. Dies sollte allerdings jedes nicht triviale Projekt ohnehin haben.
  2. Die Tests werden dadurch isoliert, dass jeder Test einen separaten „Namensraum“ benutzt. Hier hängt es vom Domänenmodell ab, ob diese Strategie genutzt werden kann. Kurzes Beispiel: Angenommen, wir haben ein Kunden Objekt, dem ein oder mehrere Bestellungen zugeordnet sind, denen wiederum Artikel zugeordnet sind. Ein Kunde interagiert immer nur mit seinen eigenen Bestellungen und sieht auch nur diese. In einem solchen Fall könnte jeder Test im ersten Schritt einen neuen Kunden anlegen (mit einer neuen, eindeutigen ID) um so die Tests voneinander zu isolieren.

Wir verwenden die erste Strategie und löschen vor jedem Test per Storra die gesamte Hörbuch-Sammlung. Dazu wird Folgendes zu features/support/hooks.rb hinzugefügt:

Before do
  delete_database backend_url('audiobooks')
end

Und die Methode delete_database wird in features/support/storage.rb implementiert:

def delete_database(url)
  RestClient.delete url
end

Hinweis: backend_url ist in features/support/env.rb definiert und gibt die URL zurück, unter der die Collection 'audiobooks' von Storra verwaltet wird.

Weitere Tests für die Liste der Hörbücher

Hinweis: Der Code des folgenden Szenarios ist per git checkout 05_filter verfügbar. Dieser Branch enthält auch den im vorigen Abschnitt besprochenen Hook zum Löschen der Datenbank.

Wenn man sich im Audio Book Collection Manager etwas umschaut, fällt einem evtl. das Texteingabefeld mit dem Label „Filter“ auf. Wenn man anfängt, in diesem Feld etwas zu tippen, werden nur noch Hörbücher angezeigt, die zu dem Suchbegriff passen. Tippt man also z. B. „Cor“, wird das Hörbuch „Coraline“ angezeigt, alle anderen Titel nicht.

Um dies in einem Cucumber-Szenario auszudrücken, können wir den folgenden Code zu features/list_audiobooks.feature hinzufügen:

  Scenario: Filter the list
    Given some audiobooks in the collection
    When I visit the list of audiobooks
    And I search for "Cor"
    Then I only see titles matching the search term
    When I remove the filter
    Then I see all audiobooks again

Um die Steps zu implementieren, fügen wir Folgendes zu features/step_definitions/audiobooks.rb hinzu:

When /^I search for "(.*?)"$/ do |search_term|
  fill_in('filter', :with => search_term)
  @matching_titles = ['Coraline']
  @not_matching_titles = ['Man In The Dark', 'Siddharta']
end
 
When /^I remove the filter$/ do
  # funny, '' (empty string) does not work?
  fill_in('filter', :with => ' ')
  @matching_titles = @not_matching_titles = nil
end
 
Then /^I see all audiobooks(?: again)?$/ do
  page.should have_content 'Coraline'
  page.should have_content 'Man In The Dark'
  page.should have_content 'Siddhartha'
end
 
Then /^I only see titles matching the search term$/ do
  @matching_titles.each do |title|
    page.should have_content title
  end
 
  @not_matching_titles.each do |title|
    page.should have_no_content title
  end
end

Betrachten wir die Step Definitions eine nach der anderen.

  • When I search for...: Die erste Zeile (fill_in('filter', :with => search_term)) benutzt Capybaras API um einen Wert in das Texteingabefeld einzutragen. Der Wert wird durch die Capturing Group des reguläre Ausdrucks festgelegt. Wird der Step also mit When I search for "Foobar" aufgerufen, würde „Foobar“ in das Eingabefeld eingetragen. Die nächsten beiden Zeilen setzen die Instanzvariablen @matching_titles und @not_matching_titles. Hier werden also Erwartungen (oder besser: erwartete Werte) in einem When-Step definiert, damit diese später in einem Then-Step überprüft werden können. In diesem Fall werden die erwarteten Werte direkt im nächsten Schritt geprüft. Diese Vorgehensweise mag diskussionswürdig sein, da Erwartungen in einem When-Step eigentlich nichts zu suchen haben, dafür ist ja der Then-Step da. Allerdings erlaubt diese Technik oft auf pragmatische Art und Weise, einen Then-Step mit verschiedenen Erwartungen (die dann in verschiedenen When-Steps definiert werden) zu benutzen. In diesem speziellen Fall gibt es allerdings auf jeden Fall noch Verbesserungspotenzial, da nur der Text für fill_in variabel ist (die Variable search_term, aus der Capturing Group), die Werte für @matching_titles und @not_matching_titles jedoch fest sind.
  • Then I only see titles matching the search term: Dieser Schritt benutzt die erwarteten Werte, die im When-Step festgelegt wurden. Wir überprüfen, dass alle Hörbuch-Titel, die zum Filter passen, angezeigt werden. Ebenso überprüfen wir für die Titel, die nicht zum Filter passen, dass sie auch tatsächlich nicht angezeigt werden.
  • When I remove the filter: Dieser Schritt entfernt den eingegebenen Text wieder aus dem Filter-Feld.
  • Die Step-Definition Then /^I see all audiobooks(?: again)?$/ do ist unsere bereits existierende Step-Definition (Then /^I see all audiobooks/ do), nur dass der reguläre Ausdruck mit einer optionalen Non-Capturing Group (?: again)? erweitert wurde, damit sich das Szenario flüssiger liest. (Wenn der obige Code per Copy and Paste in die vorhandene audiobooks.rb eingefügt wurde, muss die alte Step-Definition ohne erweiterten regulären Ausdruck gelöscht werden, um mehrdeutige Steps zu vermeiden.)

AJAX-Funktionalität mit Capybara testen

Lehnen wir uns einen Moment zurück – was genau passiert in dem Test, den wir gerade implementiert haben? Die getestete Anwendung ist mit AngularJS implementiert, also eine Single Page Application. Es findet keine Navigation statt, die Seite wird nie neu geladen. Alles passiert auf einer Seite durch asynchrones JavaScript, DOM-Manipulation und asynchronen Datenaustausch mit dem Backend (Storra). Die Filter-Funktionalität, die wir im letzten Abschnitt getestet haben passiert zum Beispiel komplett client-seitig. Wenn sich der Filter ändert, werden einige Einträge aus dem DOM entfernt, andere werden ggf. wieder angezeigt.

Hat uns das das Testen erschwert? Waren die Steps dadurch komplizierter zu implementieren? Haben wir speziellen Code benötigt, um auf die asynchronen Teile zu warten? Nein! Wir haben uns bis jetzt eigentlich noch gar keinen Gedanken darum gemacht. Der Grund dafür ist folgender: Capybara nimmt einfach grundsätzlich an, dass in einer modernen Web-Anwendung potenziell alles asynchron sein kann. Immer, wenn man per Capybara überprüft, ob bestimmte Inhalte da sind oder eine gewisse Bedingung erfüllt ist, wartet Capybara darauf, dass der Inhalt erscheint oder die Bedingung erfüllt ist, anstatt es nur einmalig zu überprüfen.

Das ist genau das Verhalten, dass ich von einer guten Browser-Abstraktionsschicht heute erwarte: Wenn ich Akzeptanztests schreibe, will ich nicht darüber nachdenken müssen, ob die Anwendung den erwarteten Inhalt durch Navigation zu einer anderen Seite oder durch ein partielles DOM-Update erzeugt. Ich will nur testen, ob der Inhalt da ist. Des Weiteren will ich meinen Test nicht anpassen, wenn die Anwendung die Inhalte morgen auf andere Art und Weise erzeugt.

Capybara erfüllt diese Erwartungshaltung. Eine ganze Reihe andere Test-Frameworks für Web-Anwendungen sind weit davon entfernt, insbesondere die Frameworks, die auf einer niedrigeren Abstraktionsstufe operieren, wie zum Beispiel Selenium. Wenn der Test-Code mit Anweisungen der Art waitForXyz oder sogar dem haarsträubenden (weil willkürlichen) sleep 2s übersät ist, benutzt man vermutlich das falsche Test-Framework.

Weitere Cucumber-Features und Scenarios

Wir könnten nun noch eine ganze Menge weitere Features und Scenarios für den Audio Book Collection Manager schreiben. Der Branch master des Beispiel-Cucumber-Projekts enthält weitere Features, zum Beispiel für das Hinzufügen eines neuen Hörbuchs oder zum Anzeigen detaillierterer Informationen zu einem Eintrag. Mit git checkout master kann man sich diese anschauen. Wer das Tutorial bis hierhin durchgearbeitet hat, dem werden in den zusätzlichen Features und Step-Files wohl keine allzu großen Überraschungen mehr begegnen.

Interessanter wäre es ggf., auf eigene Faust ein paar Cucumber-Features zu schreiben. Wie wäre es mit einem Feature für das Löschen von Hörbüchern? Oder für die Anzeige des CD-Covers eines Hörbuchs? Die entsprechende Funktionalität ist in der Anwendung bereits implementiert, es fehlt nur noch der Cucumber-Test. (Das Cover-Bild wird in der Detail-Ansicht automatisch angezeigt, falls eine ASIN eingetragen ist und amazon.com ein Bild für diese ASIN liefert).

Fazit

Damit endet unser kleiner Ausflug in die Welt von Cucumber und Capybara. Dieses Tutorial zeigt natürlich nur einen kleinen Ausschnitt von dem, was möglich ist. Ich hoffe, es wurde trotzdem deutlich, dass diese Kombination recht mächtig ist und sich damit sehr elegante und lesbare Akzeptanztests schreiben lassen.

Credits

Ein Teil des Setup-Codes (insbesondere das Umschalten zwischen Poltergeist/PhantomJS und Selenium WebDriver/Firefox durch eine Umgebungsvariable) wurde von Ruby-Guru Michael Schuerig geschrieben. Durch Michael bin auch zum ersten Mal mit Capybara und Poltergeist in Berührung gekommen.

Bastian Krol entwickelt seit über 15 Jahren Enterprise-Systeme und Open-Source-Software. Seine Schwerpunkte sind Java, JavaScript, Node.js und Hypermedia-APIs.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.