Funktionale Webtests mit Geb - JAXenter
Ein Groovy-Framework für den automatisierten Zugriff auf Webseiten

Funktionale Webtests mit Geb

Andre Steingress

Geb (ausgesprochen: Jeb) ist ein in Groovy implementiertes Framework für den automatisierten Zugriff auf Webseiten. Obwohl es als generelles Automatisierungsframework für den Zugriff auf Webseiten angesehen werden kann, liegt der Fokus vor allem in der Unterstützung zur Erstellung funktionaler Tests von Webanwendungen. Dieser Artikel führt den Leser in die grundlegenden Features von Geb und die dahinter liegenden Prinzipien ein.

Ein einfacher Anwendungsfall

Wir starten unsere Einführung in Geb mit der Realisierung eines sehr einfachen funktionalen Testfalls, der uns jedoch als gutes Einstiegsbeispiel dient. Wie in Abbildung 1 zu sehen ist, befindet sich auf der Geb-Homepage ein Navigationsmenü. Anforderung ist nun, dass zu jedem Zeitpunkt genau ein aktives Menüelement in diesem Navigationsmenü vorhanden sein muss. Bei einem Wechsel auf einen anderen Menüpunkt muss nach dem erneuten Laden der Seite der ausgewählte Menüpunkt aktiv gesetzt sein.

Abb. 1: Das Navigationsmenü auf http://geb.codehaus.org

Um diesen Anwendungsfall in ein bestehendes Testumfeld einzubetten, bietet Geb zum derzeitigen Zeitpunkt (Dezember 2010) folgende Integrationsmöglichkeiten:

  • Ausführung über separates Groovy-Skript
  • Integration in JUnit 3 Test Suite
  • Integration in JUnit 4 Test Suite
  • Integration in Spock
  • Integration in EasyB
  • Integration in Grails-Anwendung

Die Testfallerstellung wird unabhängig von der für die Applikationserstellung verwendeten Technologie in Groovy vorgenommen. Für unseren Anwendungsfall nehmen wir an, dass im Projekt bereits Spock-Spezifikationen vorhanden und auch unser neuer Anwendungsfall mithilfe der Geb-Spock-Integration implementiert werden soll.

Eine Spock-Kurzeinführung

Spock ist ein junges, aber rasant an Popularität gewinnendes Test- und Spezifikationsframework, das bestimmte Sprachfeatures von Groovy benutzt, um besonders lesbare und in weiterer Folge auch wartbare Tests bzw. Spezifikationen zu erstellen. Ein einfaches Codebeispiel ist in Listing 1 angeführt. Grundlegendes Element in Spock sind – analog zu JUnit-Testfällen – Spezifikationen (Specifications). Eine Spezifikation testet ausgewählte Features der zugrunde liegenden Komponente(n). Analog zur JUnit-Klasse TestCase werden Spock-Spezifikationen von der Klasse UnitSpec abgeleitet.

Jede Methode einer UnitSpec-Subklasse wird als Featuremethode bezeichnet. Der Aufbau einer Featuremethode ist durch verschiedene Codeblöcke vorgegeben, wobei jeder Codeblock für eine bestimmte Phase der Testausführung verwendet wird. Für eine weitere Einführung in Spock sei auf die Spock-Basics-Dokumentation verwiesen.

class StackSpec extends UnitSpec {

    def "pushing an element increases the stack's count"()  {

        setup: "create an empty stack"
           def stack = []
        when: "pushing a single element onto the stack"
    stack.push(1)
        then: "the stack's size must be 1"
    stack.size() == 1
    }
}
Ein erster Geb-Test

Um unseren Anwendungsfall zu testen, erstellen wir als ersten Schritt eine Spezifikationsklasse. Von Geb wird uns eine spezielle Klasse für die Spock-Integration angeboten: GebSpec.

In der davon abgeleiteten Spezifikationsklasse GebHomepageSpec muss nun die Methode getBaseUrl überschrieben werden, um damit den Basis-URL für die Testausführung anzugeben (Listing 2). Relativ zu diesem Basis-URL erfolgen alle Requests innerhalb dieser Spezifikation.

Class GebHomepageSpec extends GebSpec {
String getBaseUrl()  { "http://geb.codehaus.org" }
}

Als nächsten Schritt werden wir eine Featuremethode hinzufügen, die das Vorhandensein eines aktiven Menüelements bei einem Aufruf von „/“ testet (Listing 3).

class GebHomepageSpec extends GebSpec {
String getBaseUrl()  { "http://geb.codehaus.org" }
 
def "calling the homepage results in active menu element"()  {
    when:
        to "/"
    then:
  def menu_elements = $("div#navigation ul li a")
  def active_menu_elements = menu_elements.filter(".active")

  active_menu_elements.size() == 1

       def active_menu_element = active_menu_elements[0]

       active_menu_element.text() == "Home"

}
}

Sehen wir uns den Testfall etwas genauer an. Im HTML-Quellcode der Homepage wird das Navigationsmenü mit dem HTML aus Listing 4 dargestellt.

Wie daraus zu erkennen ist, wird das aktive Menüelement mit der CSS-Klasse active gesondert markiert. Um nun automatisiert auf den HTML-Code der Seite zuzugreifen, bedarf es einer Browserimplementierung, die den HTML-Code auch entsprechend interpretiert und den zugehörigen DOM im Speicher aufbaut. Glücklicherweise kann sich Geb für die Ausführung eines Browserprogramms bzw. die Emulation eines Browsers (über HtmlUnit) auf die Selenium WebDriver API ( hier und hier) stützen. Die WebDriver API bietet einheitliche Schnittstellen gegenüber verschiedenen Browserimplementierungen (Internet Exporer, Firefox, Chrome, HtmlUnit), mithilfe derer browserunabhängige Testfälle umgesetzt werden können.

Geschrieben von
Andre Steingress
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.