Schmerzfreies Webtesting mit Canopy und F#

24 November 2016
Alexander Baier

Canopy ist eine domänenspezifische Sprache (DSL) zum Testen von webbasierten Benutzeroberflächen. Sie ist implementiert als zusätzliche Funktionen und Operatoren in F#. Um die verschiedenen Browser zu steuern bedient sich Canopy der DotNet-API des Selenium Projektes.

Der grundlegende Aufbau eines Tests sieht wie folgt aus. Nach dem man Canopy angewiesen hat einen oder auch mehrere Browser zu starten, definiert man verschiedene Aktionen. Dies sind häufig Aktionen, wie sie auch von einem echten Benutzer der Webseite ausgeführt werden. Beispiele sind das Ausfüllen von verschiedenen Eingabeelementen, das Klicken auf Buttons, oder das allgemeine Navigieren auf der Seite. Ein Test endet mit dem Aufzählen einer oder mehreren Assertions, welche den Erfolg des Tests definieren.

module Program

open canopy
open runner

// utility function
let predicate f = fun x ->
    try
        f x
        true
    with _ -> false

let displayedP item = predicate displayed item

//start an instance of the firefox browser
start firefox

context "Interap Navigation"

before <| fun _ ->
    url "https://interap.de"

"""Navigiere zu "Unternehmen" """ &&& fun _ ->
    click "Unternehmen"
    displayed "div.intro"
        
"""Navigiere zu "Leistungen" """ &&& fun _ ->
    click "Leistungen"
    displayed "Anforderungsanalyse"
    displayed "Konzeption"
    displayed "UI / UX"

"""Navigiere zu "Blog" """ &&& fun _ ->
    click "Blog"
    waitFor2 "Waiting for \"Tags\" to be displayed" (fun _ -> displayedP "span.fa-tags")
    title() === "Blog | INTERAP"


run()

printfn "press [enter] to exit"
System.Console.ReadLine() |> ignore

quit()

Der oben stehende Code-Auschnitt zeigt drei Testdefinitionen, welche das Navigieren auf unserer Webseite Testen. Die context Funktion setzt den aktuellen Kontext, welche für die folgenden Testdefinitionen gilt, solange noch kein neuer Kontext deklariert wurde. Ein Kontext dient dazu, mehrere Tests konzeptionell zusammenzufassen, womit sich auch das Reporting übersichtlicher gestaltet.

Die before Funktion wird einmal vor jedem Test ausgeführt. Analog dazu gibt es noch after, once, lastly, dessen Namen ziemlich selbsterklärend sind. In unserem Fall rufen wir vor jedem Test die URL unserer Webseite auf. Der Operator &&& definiert einen neuen Test. Hierbei spezifiziert der linke Operand den Testnamen und der rechte Operand die Aktionen und Assertions welche den Test definieren. Die Aktionen und Assertions arbeiten mit Selektoren um bestimmte Elemente auf der Webseite zu finden. Canopy unterstützt verschiedene Selektoren. In unserem Beispiel haben wir gebrauch vom Text- und von dem Css-Selektor gemacht. Ersterer benutzt jQuery im Hintergrund um Elemente rauszusuchen, deren Text-Wert mit dem Argument des Selektors übereinstimmt. Letzterer kann mit ganz normalem CSS umgehen, so wie wir es bei der Assertion in unserem ersten Test gesehen haben: "div.intro". Canopy bietet noch weitere Selektoren an. So wäre es Beispielsweise möglich jQuery direkt zu benutzen, oder gar auf XPath zurückzugreifen. Somit würde click "a[id^='btn']" auf alle a Elemente klicken, welche eine id haben, die widerum mit "btn" beginnt.

Zuletzt möchte ich auf den dritten Test hinweisen, welcher aufzeigt, wie man Canopy sehr schnell erweitern kann. Da die Testdefinitionen normale F#-Programme sind, steht uns in Canopy-Tests die Funktionlität einer kompletten Programmiersprache zur Verfügung. In unserem Test habe ich warten wollen bis das Tags-Element angezeigt wird, bevor ich den Titel der Webseite überprüfe. Die Assertion displayed gibt es bereits, sie hat allerdings das falsche Verhalten, um als Argument für waitFor benutzt zu werden. Hier brauchen wir eine Funktion welche einen boolschen Wert liefert. Um diese Diskrepanz zu überbrücken, habe ich die Funktion predicate geschrieben, welche eine andere Funktion als Parameter bekommt, diese ausführt und nur dann true zurück gibt, wenn keine Exception geworfen wurde. Mit predicate habe ich nun die Möglichkeit eine Assertion in eine Predikatsfunktion umzuwandeln, um diese dann mit waitFor einzusetzten.

Zusammenfassend ist Canopy eine einfache und schnelle Möglichkeit Automation jeglicher Art im Browser umzusetzen. Wenn Sie interesse haben, sich tiefergehend mit Canopy zu beschäftigen, empfehle ich ihnen einen einführenden Vortrag von Chris Holt, dem Autor dieses Tools. Weitere Informationen, sowie Installationshinweise und Dokumentation befinden sich auf der Homepage.

  .NET  Automatisieren  CSS3  HTML5  jQuery  UX