Automatische Tests des User Interface einer Web Applikation: Se Builder automatisieren (Teil 6)

von Roland Rickborn

Diese 6-teilige Blogpost-Reihe befasst sich mit dem Thema Test-Automatisierung für grafische Benutzeroberflächen von Webanwendungen. Im Wesentlichen kommt die Selenium Test Tool Suite zum Einsatz. Im Speziellen wird der Umgang mit dem Tool Se Builder erklärt, mit dem Tests aufgenommen und abgespielt werden können.

Der ersten Teil [1] gab eine kurze Einführung zum Tool Selenium Builder, die Installation sowie einen Vergleich des Tools mit der Selenium IDE. Im zweiten Teil [2] haben wir das Selenium JSON-Format untersucht und eine Test-Suite erstellt. Im dritten [3] und vierten Teil [4] habe ich Einblicke in meine Arbeit gegeben und versucht, Fallstricke aufzuzeigen.

Im letzten Teil dieser Reihe will ich demonstrieren, wie man Testläufe im CI Server Jenkinks mit Selenium Grid automatisieren kann. 

Beschreibung Testumgebung

Uns steht ein Selenium Grid mit einem Hub und 4 Nodes zur Verfügung. Beim Hub handelt es sich gleichzeitig um unseren Jenkins Master, er hat ein Linux Betriebssystem und dient in unserem Fall nur zur Verteilung der Jobs. Die Nodes laufen unter Windows Vista bzw. Windows 7. Es handelt sich um VMs von modern.ie, jeweils mit einer unterschiedlichen Version des Internet Explorers. Die vier VMs haben die Namen exensiovistaie7, exensiowin7ie8, exensiowin7ie9 und exensiowin7ie10. Bei der zu testenden Applikation handelt es sich um unser Claret Portal. Die Testumgebung ist nur in unserem Intranet verfügbar. Alle VMs sind wie im Teil 5 dieser Blogpost Serie beschrieben vorbereitet. Das bedeutet, alle VMs: 

  • sind gestartet
  • es ist unser Standardbenutzer eingeloggt
  • alle Programme im Autostart Verzeichnis des Standardbenutzer wurden ausgeführt
    • Insbesondere also Selenium Grid (mit der Rolle Node)
    • VNC Server
  • haben vorbereitete Einstellungen für den Internet Explorer (identische Sicherheitseinstellungen, Updates deaktiviert, etc.)

Anforderungen

Ich gehe davon aus, dass Testfälle in den meisten Fällen mit dem Selenium Builder aufgezeichnet werden. Danach wird manuell geprüft, ob die so erstellen Testfälle lokal und remote wie erwartet durchlaufen. Laufen die Testfälle stabil, können sie Jenkins übergeben werden. Mit dieser Anforderung werden direkt die mit Selenium Builder erstellen JSON Dateien zum Test herangezogen. Die Anforderung hat den Vorteil, dass kein Export der JSON Dateien erforderlich ist. Möchte man stattdessen z. B. mit Java/JUnit testen, wäre eine Konvertierung des JSON Skripts notwendig. Die erforderliche Export-Funktion bringt Selenium Builder bereits mit. Folgende Exporte sind möglich:

Aktuell sind noch nicht alle Funktionen des Selenium Builders in die Export-Funktion eingebaut. Dadurch kommt es häufig zu Einschränkungen beim Export. Ein relativ simples JSON Skript für den Logout vom Claret Portal führt beispielsweise dazu, dass einige Exportformate nicht mehr möglich sind.

Testfall Remote ausführen

Um die Integration von Selenium Builder bzw. Grid in Jenkins zu demonstrieren, habe ich einen relativ einfachen Testfall erstellt. Er besteht aus folgenden Komponenten: 

  • JSON Suite test_blogpost_teil_6_suite.json mit den Skripten
    • test_blogpost_teil_6_login.json
    • test_blogpost_teil_6_doSomething.json
      • ruft test_blogpost_teil_6.csv auf
    • test_blogpost_teil_6_logout.json

Der Testfall kann jederzeit mit dem Selenium Builder gestartet und lokal oder remote ausgeführt werden. Der Ablauf beginnt mit dem Einloggen in die zu testende Applikation. Danach wird das „doSomething“ Skript aufgerufen, welches seinerseits die CSV Datei lädt und darin befindliche Parameter einliest. In unserem Fall wird der Parameter retval eingelesen, nachdem anschließend in der Applikation gesucht wird. Am Ende des Testfalls steht der Logout aus der Applikation. 

Hinweis CSV Datei: Data-Driven Testing

Selenium Builder bietet die Möglichkeit, externe Variablen einzulesen. Zu finden ist die Funktion unterhalb des Data Menüs. Die Funktion bietet sich an für datenbezogene Tests, wenn eine Reihe von Testfällen mit unterschiedlichen Variablen gefahren werden soll. Zur Verfügung stehen die folgenden Eingabeformate: JSON, XML und CSV:

Ich zeige anhand des folgenden Beipsiel-Skripts, wie auf externe Variablen zugegriffen werden kann und in welcher Form sie eingelesen werden können. Zuerst das Skript für JSON Input Data:

{
  "type": "script",
  "seleniumVersion": "2",
  "formatVersion": 2,
  "steps": [
    {
      "type": "print",
      "text": "${retval}"
    }
  ],
  "data": {
    "configs": {
      "manual": {
        "retval": "exensio"
      },
      "csv": {
        "path": "test_blogpost_teil_6_data.csv"
      }
    },
    "source": "csv"
  },
  "inputs": [
    [
      "retval",
      "string"
    ]
  ],
  "timeoutSeconds": 60
}

Die zugehörige JSON Daten-Datei sieht wie folgt aus:

[ {"retval": "exensio"}, 
  {"retval": "consulting"}, 
  {"retval": "claret"},
  {"retval": "selenium"}
]

Die externen Daten, ein eindimensionales Array, wird als Variable “retval” eingelesen. Im Selenium Builder Skript kann auf die Variable mit ${retval} zugegriffen werden. Auf dieselbe Weise kann man XML- und CSV-Daten verwenden. Die Formate sind wie folgt:

retval
exensio
consulting
claret
selenium

und

<testdata>
<test retval="exensio">
<test retval="consulting">
<test retval="claret">
<test retval="selenium">
</testdata>

Natürlich muss im Selenium Builder Skript jeweils der Dateinamen und der Wert von Source angepasst werden. In den oben gezeigten Fällen haben alle Input Data-Dateien jeweils 4 Werte bzw. Schlüssel-Wert-Paare. Je Wert wird der komplette Testfall einmal durchlaufen. Bei meinem Testfall führt dies also zu vier Durchläufen.

Hinweis WebDriver Capabilities

Das Verhalten des WebDriver (aller Klassen von WebDriver) kann über die sog. Capabilities gesteuert werden. Die wichtigsten Capabilities, speziell für den InternetExplorerDriver sind: 

  • browserAttachTimeout [5]
    "Gets or sets the amount of time the driver will attempt to look for a newly launched instance of Internet Explorer."
  • enablePersistentHover [6]
    "Gets or sets a value indicating whether to enable persistently sending WM_MOUSEMOVE messages to the IE window during a mouse hover."
  • ie.forceCreateProcessApi [7]
    "Gets or sets a value indicating whether to force the use of the Windows CreateProcess API when launching Internet Explorer. The default value is false."
  • ie.usePerProcessProxy [8]
    "Gets or sets a value indicating whether to use the supplied Proxy settings on a per-process basis, not updating the system installed proxy setting. This property is only valid when setting a Proxy, where the Kind property is either Direct, System, or Manual, and is otherwise ignored. Defaults to false. "
  • ignoreZoomSetting
    -
  • handlesAlerts [9]
    "Capability name used to indicate whether the browser can handle alerts."
  • nativeEvents [10]
    "Gets or sets a value indicating whether to use native events in interacting with elements."
  • ie.ensureCleanSession [11]
    "Gets or sets a value indicating whether to clear the Internet Explorer cache before launching the browser. When set to true, clears the system cache for all instances of Internet Explorer, even those already running when the driven instance is launched. Defaults to false."
  • elementScrollBehavior [12]
    "Gets or sets the value for describing how elements are scrolled into view in the IE driver. Defaults to scrolling the element to the top of the viewport."
  • ie.browserCommandLineSwitches
    -
  • requireWindowFocus [13]
    "Gets or sets a value indicating whether to require the browser window to have focus before interacting with elements."
  • takesScreenshot [14]
    "Indicates a driver that can capture a screenshot and store it in different ways."
  • javascriptEnabled 
    -
  • ignoreProtectedModeSettings 
    -
  • enableElementCacheCleanup 
    -
  • cssSelectorsEnabled 
    -
  • unexpectedAlertBehaviour [15]
    "Determines how unhandled alerts should be handled."

Die Beeinflussung des Verhaltens des Browsers ist mit Vorsicht zu genießen. Manche Optionen sind hilfreich, andere führen zu Abstürzen. Beim Claret Portal kommt das Spring Security Framework zum Einsatz. Mit der Option ie.ensureCleanSession=true möchte ich sicherstellen, dass jeder Testlauf mit leerem Cache startet und auf diese Weise auch der erfolgreiche Login inkl. Cookie und Session getestet wird. Andererseits führte die Verwendung der Option browserAttachTimeout=<integer> zu sofortigen Abstürzen des WebDrivers.

Testaufbau

Für den automatischen Start der Testfälle in Jenkins verwende ich das Tool SeInterpreter [16] in Verbindung mit einem parametrisierten Free Style Job und einem Shell-Aufruf als Buildverfahren. Als Parameter habe ich angelegt:

Eigentlich würden schon die beiden ersten Parameter ausreichen, da ich ja jeweils nur Browser und Version steuern können möchte. Über den Parameter script gebe ich den Pfad zu meiner JSON Suite von Selenium Builder an. Denkbar wäre an der Stelle aber auch den Pfad zu einem Repository zu hinterlegen und somit tatsächliche kontinuierliche Integration zu gewährleisten.

Natürlich erlaube ich in meinem Job die Parallele Ausführung der Builds.

Als Build-Auslöser habe ich die Option "skriptgesteuert" gewählt und dafür einen Token angelegt

Mit dieser Lösung kann ich später meinen Testfall an unseren DevJob für das Claret Portal knüpfen. Beispielsweise, nachdem das Portal erfolgreich gebaut wurde und alle Unit- und Integrations-Tests durchlaufen wurden, schließen sich die Funktionalen Tests an.
Anstatt des Shell Aufrufs als Buildverfahren wäre auch der Einsatz des Selenium Builder Plugins [17] denkbar. Die Funktionalität dürfte identisch sein.

Mit diesen Einstellungen ist der Testfall bereits vorbereitet für die parallele Ausführung. Für den WebDriver sind parallele Instanzen kein Problem. Allerdings sollte man auf die maximale Anzahl gleichzeitig ablaufender Instanzen auf einem Node achten. Dazu haben wir in Teil 5 der Blogpost Serie die Selenium Grid Nodes mit einer JSON Konfigurationsdatei gestartet.

Zur Verteilung der Testfälle bzw. für den parallelen Start mehrerer Testfälle auf unterschiedlichen Nodes lege ich mir einen weiteren Jenkins Job an, dieses Mal vom Typ Build Flow. Natürlich muss vorher das zugehörige Build Flow Plugin [18] installiert werden. Die Einstellungen dieses Jobs sind sehr simpel, einfach den Flow angeben:

Mit diesen Einstellungen starte ich bei jedem Aufruf des Jobs meinen Testfall fünf Mal parallel. Der Testfall wird mit vier unterschiedlichen Internet Explorer Versionen und einmal mit einem Firefox gestartet. Dabei wird die Firefox Instanz auf einem beliebigen Node ausgeführt. Die IE Instanzen werden parallel auf den jeweiligen Nodes ausgeführt.

Hinweis Stabilität

Die vorgestellte Lösung kann leider nicht als stabil angesehen werden. Trotz einiger Tweaks in Verbindung mit dem InternetExplorerDriver von Selenium kommt es relativ häufig vor, dass sich ein Test aufhängt. Speziell der IE8 bleibt dann scheinbar grundlos stehen bzw. der Test wird nicht sauber beendet. Selbst unter Zuhilfenahme des Build-timeout Plugins [19] gelingt es nicht, das Problem in den Griff zu bekommen. Das Build-timeout Plugin sorgt zwar dafür, dass abgebrochene Jobs auf Jenkins-Seite irgendwann gestoppt und als fehlgeschlagen markiert werden. Es führt aber nicht dazu, dass der laufende Job auf dem Node ebenfalls abgebrochen wird. Je nach Node-Einstellung (Anzahl zu akzeptierten Instanzen), nimmt der Node evtl. keine neuen Jobs mehr an. Das problematische Verhalten des InternetExplorerDrivers bzw. des Internet Explorers selbst ist bekannt. Eine mögliche Lösung könnte von Selenium-Grid-Extras [20] kommen. In dem Projekt wird eine Erweiterung für Selenium Grid entwickelt, die das aktive Beenden aller IE Instanzen auf einem Node ermöglicht. Eine genauere Untersuchung folgt hier in einem neuen Blogpost.

Links

[1] Automatische Browsertests mit Selenium: Zielvorstellung und Einführung Selenium Builder (Teil 1)
[2] Automatische Browsertests mit Selenium: JSON Dateiformat und Test-Suite (Teil 2)
[3] Automatische Browsertests mit Selenium: Tipps und Tricks mit Se Builder (Teil 3)
[4] Automatische Tests des User Interface einer Web Applikation: Tipps und Tricks mit Se Builder (Teil 4)
[5] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_BrowserAttachTimeout.htm
[6] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_EnablePersistentHover.htm
[7] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_ForceCreateProcessApi.htm
[8] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_UsePerProcessProxy.htm
[9] https://selenium.googlecode.com/svn/trunk/docs/api/dotnet/html/F_OpenQA_Selenium_Remote_CapabilityType_HandlesAlerts.htm
[10] http://www.nudoq.org/#!/Packages/Selenium.WebDriver/WebDriver/InternetExplorerOptions/P/EnableNativeEvents
[11] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_EnsureCleanSession.htm
[12] https://selenium.googlecode.com/git/docs/api/dotnet/html/P_OpenQA_Selenium_IE_InternetExplorerOptions_ElementScrollBehavior.htm
[13] http://www.nudoq.org/#!/Packages/Selenium.WebDriver/WebDriver/InternetExplorerOptions/P/RequireWindowFocus
[14] https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/TakesScreenshot.html
[15] https://stackoverflow.com/questions/12913453/how-to-handle-an-alert-with-unexpectedalertbehaviour-capability-in-selenium
[16] https://github.com/sebuilder/se-builder/tree/master/tools/seinterpreter
[17] https://wiki.jenkins-ci.org/display/JENKINS/Selenium+Builder+Plugin
[18] https://wiki.jenkins-ci.org/display/JENKINS/Build+Flow+Plugin
[19] https://wiki.jenkins-ci.org/display/JENKINS/Build-timeout+Plugin
[20] https://github.com/groupon/Selenium-Grid-Extras

Kategorien: SeleniumTesting

Zurück