Türchen 17: Funktionales Testen und Continues Integration in 5 Schritten

Viele Entwickler schrecken davor zurück, funktionale Tests zu nutzen. Hauptargumente sind die langen Ausführungszeiten und das aufwendige Setup. Die langen Ausführungszeiten sind allerdings akzeptabel, wenn die Tests unabhängig von der Entwicklung auf einem entfernten Rechner ausgeführt werden. Das Setup möchte ich hier in fünf einfachen Schritten beschreiben.

Voraussetzung für alle weiteren Schritte ist ein Versionsmanagement – genauer SVN oder Git. Es wird ein Versionsmanagement benötigt, auf den die Continues Integration zugreifen kann – ein interner GIT-Server taugt hier zum Beispiel ebenso wie GitHub oder Bitbucket.

Schritt 1: Selenium

SeleniumIDE

Die Selenium IDE ist vielen bekannt und ein denkbar einfach zu bedienendes Plugin für Firefox. Mit dem Plugin kann Interaktion mit einer Webseite aufgezeichnet und später wieder ausgeführt werden. Bei komplexeren Webseiten sind vor allem zwei Arten von Nacharbeiten notwendig: Werden Ajax-Request ausgeführt, so muss Selenium mitgeteilt werden, woran der erfolgreiche Abschluss erkannt werden kann. Außerdem müssen gelegentlich die Pfade der HTML-Elemente überarbeitet werden, um ein stabiles Ergebnis zu erhalten. Lässt sich der Selenium-Test erfolgreich ausführen, ist Schritt 1 abgeschossen. Das Ergebnis wird als Selenium-HTML-Datei gespeichert und in Schritt 5 wieder benötigt.
Weiterführende Informationen zu Selenium:

Schritt 2: Ubuntu aufsetzen

Proxmox-650x584
Nun benötigen wir einen Server mit einem aktuellen Ubuntu; auch eine virtuelle Maschine reicht in aller Regel aus. Natürlich funktionieren auch andere Linux-Systeme, die allerdings teilweise mit anderen Paket-Managern daherkommen. Entsprechend werden dann auch andere Befehle zur Installation benötigt. Auf dem System müssen wir nun imagemagick, git, java und ggf. firefox installieren. Dazu benötigen wir in einer Shell die folgenden Befehle:
apt-get install imagemagick
 apt-get install git
 apt-get install openjdk-7-jdk
 apt-get install firefox
Der letzte Befehl ist nur dann notwendig, wenn Firefox nicht bereits installiert ist.

Schritt 3: Xvfb – Virtuellen Bildschirm installieren

Uhr
Das System soll mit Selenium einen Browser steuern und dieser Browser benötigt einen Bildschirm. Da wir aber (in aller Regel) keinen Bildschirm an das System anschließen können oder wollen, muss unser Bildschirm virtuell sein. Dazu installieren wir X Window Virtual Framebuffer – kurz Xvfb:
apt-get install xvfb
Den virtuellen Bildschirm testen wir zunächst mit der Dummy-Applikation xclock, die eine Uhr darstellt:
/usr/bin/Xvfb :5 -ac -screen 0 1024x768x8 &
 xclock -display :5.0 &
 xwd -root -display :5.0 -out outputfile
 convert outputfile outputfile.png
Geht alles gut, so ist die oben abgebildete Uhr im outputfile.png zu sehen. Im nächsten Schritt führen wir den gleichen Test mit Firefox durch:
/usr/bin/Xvfb :5 -ac -screen 0 1024x768x8 &
 export DISPLAY=:5.0
 firefox
 xwd -root -display :5.0 -out outputfile
 convert outputfile outputfile.png
Läuft Firefox nicht, muss es möglicherweise nachinstalliert werden. Weitere Informationen zum gesamten Schritt 3 finden sich hier: http://centripetal.ca/blog/2011/02/07/getting-started-with-selenium-and-jenkins/

Schritt 4: Hudson installieren

Hudson
Nun installieren wir das Continues Integration System: Hudson. Der Prozess sollte mit Jenkins analog funktionieren, ich habe es allerdings nicht ausprobiert. Hierzu sind ein paar Schritte notwendig, deren Bedeutung in den Links am Ende des Abschnitts nachvollzogen werden können:
sh -c "echo 'deb http://hudson-ci.org/debian binary/' > /etc/apt/sources.list.d/hudson.list"
 apt-get update
 apt-get install hudson</div>
<div>
Der Port, unter dem Hudson läuft, wird in der Datei /etc/default/hudson der Port auf 8081 umgestellt, denn den Standard-Http-Port 80 benötigen wir für Selenium. Dann wird Hudson (oder wenn nicht bekannt wie, der ganze Server) neu gestartet. Nun beginnt die Arbeit mit Hudson im Web-Browser. In unserem Fall ist Hudson unter http://c4b-hudson.c4b.local:8081/ zu finden. In Hudson selbst sollte zunächst ein Benutzerkonto angelegt werden, um den öffentlichen Zugriff zu verhindern. Dann müssen die folgenden Plugins für Hudsons nachinstalliert werden: „Seleniumhq“ und „Selenium“ HTML. Dies ist in Hudson unter „Manage Hudson“ und dann „Manage Plugins“ möglich. Schließlich benötigen wir eine aktuelle Version des Selenium Standalone Servers. Dieser ist unter http://www.seleniumhq.org/download/ zu haben und wird im Server unter /var/lib/hudson/ abgelegt. Der Besitzer der Datei muss mit chown auf hudson gesetzt werden. Zurück im Browser wird in Hudson unter Magage Hudson und Configure System die Einstellung Selenium Remote Control auf den Pfad des Selenium Standalone Servers gesetzt, also z.B. /var/lib/hudson/selenium-server-standalone-2.38.0.jar. Weiterführendes Links zu Hudson:

Schritt 5: Selenium und Hudson

Innerhalb von Hudson muss nun ein Build angelegt werden. Voraussetzung ist ein GIT-Server, der vom Hudson-Server aus zugänglich ist. Im folgenden Bild werden allgemeine Daten angelegt und der virtuelle Bildschirm als Parameter angegeben. Existieren später mehrere Tests, die parallel laufen sollen, so benötigen sie unterschiedliche Nummern für die verschiedenen Bildschirme.
HudsonSelenium1-650x450
Dann werden wird der Zugriff auf das Versionsmanagement eingetragen. Wir nutzen unser GIT per http hinter einer Firewall, so dass der Zugriff denkbar einfach ist. Bei komplizierten Zugriffen ist möglicherweise ein Blick ins Hudson-Handbuch notwendig.
HudsonSelenium2-650x283
Dann werden drei Befehle konfiguriert, die später von Hudson in der Shell ausgeführt werden und schließlich wird Selenium selbst gestartet. Der Erste Befehl startet den virtuellen Bildschirm. Der zweite und dritte Befehl löschen den Cache und setzten Berechtigungen zurück – sie können bei korrekte Konfiguration entfallen, für den Test lassen wir sie drin:
HudsonSelenium3-650x491
Voraussetzung ist, dass unter dem Pfad selenium/Hach.html aus dem Git auch wirklich eine Selenium-Datei liegt. Diese wurde in Schritt 1 erzeugt. Außerdem muss ein Zugriff auf http://c4b-hudson.c4b.local/hach/ möglich sein und auf das Projekt zeigen. Dazu muss ein Symbolischer Link Verzeichnis /var/www auf das Projekt gelegt werden. Wenn alles geklappt hat, ist es nun geschafft: Hudon zeigt die erfolgreichen Tests an:
HudsonSuccess

Fazit

Gewusst wie ist sind funktionale Tests mit Selenium in einer Continues Interation eine Aktion von wenigen Stunden von nichts bis zum funktionierenden Setup. Für einen einzelnen Entwickler von fraglichem Nutzen profitiert schon ein kleines Team von dieser Kombination sehr.


Ein Beitrag von Nikolai Krambrock
Nikolai's avatar

Nikolai Krambrock ist Diplom-Informatiker und kommt aus der "klassischen" Softwareentwicklung mit Java und C#. Seit 2009 arbeitet er mit seinem Unternehmen code4business überwiegend in der Magento-Modulentwicklung (Frontend, Backend und Schnittstellen). Daneben betreut er auch einige größere Magento-Kunden. Sie erreichen ihn unter www.krambrock.com, seine Firma unter www.code4business.de.

Alle Beiträge von Nikolai

Kommentare
Nikolai Krambrock am

Das Repository zur Installation von Hudson hat sich geändert. Es ist heißt nicht mehr: sh -c "echo 'deb http://hudson-ci.org/debian binary/' > /etc/apt/sources.list.d/hudson.list"

Sondern: sh -c "echo 'deb http://hudson-ci.org/debian /' > /etc/apt/sources.list.d/hudson.list"

Vergleiche: https://wiki.eclipse.org/Hudson-ci/Installing_Hudson_DEB

Webinar zu automatisiertem Testen mit Selenium für Magento | Magento Agentur und Entwicklung am

[…] http://www.webguys.de/magento/tuerchen-17-funktionales-testen-und-continues-integration-in-5-schritt… […]

Nikolai Krambrock am

@Don Bosco van Hoi: Vielen Dank für die detaillierten Kommentare und Erweiterungen! Für ein leistungsfähigeres Setup sind die sicher sehr hilfreich!

Don Bosco van Hoi am

Halo Nikolai,

ich finde die Webdriver-Implementierungen eine tolle Sache. Jedoch müssen in dem Fall sämtliche Tests programmatisch erstellt werden. Es gibt hier ja viele verschiedene Webdriver-APIs.

Einen großen Vorteil bietet die Selenium-IDE durchaus, und zwar die einfache Erstellung der Tests durch Klick. Wenn man jetzt noch die Möglichkeit hätte, die Tests einfach zu exportieren, so wäre das eine fantastische Geschichte, da die Tests dann einfach über das Webdriver-Protokoll ausgeführt werden können.

Hierzu gibt es momentan 3 Ansätze:

Programmatische Erstellung der Tests anhand der API Sämtliche Tests werden mit einer kompatiblen API (https://github.com/facebook/php-webdriver) geschrieben und ausgeführt. Hierzu benötigt man lediglich einen Selenium2-Server aufgrund der Webdriver-Unterstützung. Ein nettes Tutorial gibt es hier: http://codeception.com/11-12-2013/working-with-phpunit-and-selenium-webdriver.html.

Programmatische Erstellung der Tests anhand eines Frameworks, dass die API unterstützt Zufälligerweise bindet das Codeception Testing-Framework auch die Facebook-Webdriver API ein. Hier können über generische Methoden (Wrapper um Webdriver-Funktionen) relativ einfach zu verstehende Tests geschrieben werden. Die Wahl der Adapter bleibt dir überlassen. Natürlich bietet sich hier Webdriver an.

Export der zusammengeklickten Tests aus der Selenium IDE Das ist die ideale Lösung. Idealerweise klicke ich mir den Test zusammen, exportiere das zb in die Facebook-Webdriver-API kompatible Methoden und führe einfach den Test aus. Leider funktioniert das nicht, da es hierzu kein Plugin gibt (Intern habe ich bereits eine sehr eingeschränkte Erweiterung für die Facebook-Webdriver-API entwickelt und gebe diese auch gerne weiter). Eine Alternative stellt zb der Codeception-Formatter dar (https://github.com/allmyitjason/codeception-selenium-ide-format).

Zum Thema Grids:

Grids sind eine fantastische Sache, um verschiedene Browser zu testen, insbesondere wenn die Commit/Push-Anzahl in der Continues-Integration-Umgebung sehr hoch ist. Zum Beispiel könntest du ein Selenium-Grid registrieren und sagen wir mal 5 Server dran hängen. Jeder Server kann bis zu 5 Browser gleichzeitig ausführen. Dabei führt Server 1 nur Firefox aus, Server 2 nur Chrome, Server 3 Firefox und Chrome usw... Kannst du eigentlich konfigurieren wie du möchtest. Sinnvoll wird das Ganze wenn eine Testsuite so komplex wird, dass die Laufzeit der Tests größer ist als der Zeitraum bis zum nächsten Commit/Push. Im schlimmsten Fall baust du dir hiermit eine Queue auf, die so groß wird, dass jeder Build erst mal darauf warten muss, bis die Selenium-Tests durchgelaufen sind. Bei einem verteilten Selenium-Setup (Grid/Nodes) wird die Last einfach auf die nächste verfügbare Maschine mit der entsprechend gewollten Konfiguration (zb: Führe Test auf einer Firefox Maschine aus) ausgeführt. Das lohnt sich bereits bei einem sehr kleinen Team.

Grüße

Nikolai Krambrock am

@Don Bosco van Hoi: Total gute Anmerkungen! Ich habe auf alles verzichtet, was die Sache komplizierter machen könnte. Es würde mich freuen, wenn das hier in den Kommentaren ausgebaut wird.

Zum Thema Webdriver-Implementierung: Ich bin voll auf deiner Seite. Durch die Webdriver-Implementierung kann viel doppelter "Quellcode" vermieden werden, solange die Tests von den Entwicklern aufgebaut werden. Bei uns werden die Selenium-Tests allerdings durch Tester aufgebaut. Beide Entscheidungen ergeben aus meiner Sicht Sinn.

Bei den Grids bin ich mir noch nicht so sicher, wo in diesem Anwendungsfall (kleines Team, Tests werden bei jedem Commit ausgeführt) der Nutzen liegt. Vielleicht hängst du noch mal ein paar Links rein.

Don Bosco van Hoi am

Hallo Nikolai,

ihr benutzt also das HTML-File aus der IDE, um mit Selenium zu testen? Kann man durchaus machen. Hast du auch schon mal evaluiert, ob es nicht sinnvoller ist, eine Webdriver-Implementierung zu benutzen?

So wie ich es verstanden habe, wird Selenium hier als Standalone-Server benutzt, um einen lokalen Selenium-Test laufen zu lassen. Viel interessanter, auch bezüglich der Verwaltung mehrere Testinstanten ist jedoch die Konfiguration von Selenium-Server als Grid, an denen du verschiedene Knoten dranhängen kannst.

@Rokko:

Eine Integration von Vagrant mit Puppet im Jenkins ist durchaus möglich und nicht sehr kompliziert (einfach das vagrant-skript ausführen). Jedoch kann ich mir nur wenige Situationen vorstellen, die für jeden Build eine komplett neue Instanz mit Vagrant erfordern (Staging, Pre-Release). Für das Provisioning, falls das Teil des Code-Repositories ist, kann das schon sinnvoll sein. Möglich ist es allermal.

Grüße

Nikolai Krambrock am

@Rokko: Extensions für Jenkins laufen meines Wissens nach fast immer auch in Hudson und umgekehrt. Meine Anleitung sollte (nicht getestet) auch mit Jenkins laufen. Mit Vagrant haben wir bei code4business leider noch nichts gemacht.

Rokko am

Hallo Nikolai,

danke für diesen coolen Beitrag! Aus anderen Projekten kenne ich Jenkins, jedoch haben wir diese Technologie noch nicht im Magento-Umfeld im Einsatz. Ich habe gelesen, für Jenkins (vll. auch für Hudson) gibt es die Möglichkeit, Vagrant (z.B. mit Puphpet) in den Testprozess zu integrieren. Hast du hiermit schon Erfahrungen sammeln können?

Viele Grüße, Rokko

Dein Kommentar