Aus der QA-Küche 2: Beliebte Probleme mit Selenium und Jenkins

Wie im vorangegangenen Beitrag möchte ich hier zwar nichts weltbewegendes, jedoch nützliches zu Tests via Selenium für Einsteiger zum Besten geben. Heute im Automations-Kontext via Jenkins CI Server. Wie das funktioniert, dazu finden sich etliche Blog-Einträge, die aber überwiegend mehr oder weniger das gleiche (ab-)schreiben. Hier noch ein paar zusätzliche Kniffe:
Selenium html-Report
Um diesen fesch per Jenkins darzustellen (siehe Bildschirmfoto), am besten das Jenkins Seleniumhq-Plug-in installieren und in der Job-Konfiguration ein Häkchen bei „Publish Selenium Report“ setzen. In die Zeile „Test report HTMLs“ kommt dann der Workspace-relative Pfad samt Dateiname, in meinem Fall also einfach selenium.html (vgl. unten Option bash-Aufruf des Selenium-Servers).
Selenium aufrufen
Wir benötigen Selenium sowie Xvfb. Beides führe ich nicht als Plug-in aus, sondern bastel’ die Aufrufe für letzteres sowie einen Selenium-Standalone-Server mit ins bash script, simpel und ergreifend, da die Tests ohnehin einige Befehle zur Vorbereitung benötigen (d.h. in meinem speziellen Fall für icinga-web, führe ich daher hier nicht auf) und ich sowohl die Abfolge der Kommandos als auch Selenium-Parameter bequemer festlegen kann.

  • Wir fahren zunächst den Selenium server per URL herunter (falls dieser aufgrund von abgebrochenen Jobs o.Ä. noch ungewollter Weise läuft):
curl -f --connect-timeout 3 http://build.icinga.org:4444/selenium-server/driver/?cmd=shutDownSeleniumServer || :

Jenkins bricht bei Fehlermeldungen (also, wenn Selenium nicht läuft) nicht ab, da wir Error codes per || : ins naheligende Nirvana schubsen. > /dev/null 2>&1 und die „Klappe halten“-curl-Option -f reichen dafür nicht aus.

  • Selbiges funktioniert ebenso grandios mit Xvfb:
/etc/init.d/xvfb stop > /dev/null 2>&1 || :
/etc/init.d/xvfb start > /dev/null 2>&1 || :
  • Wir legen dann einen Monitor fest:
export DISPLAY=":666"
  • Und rufen den Seleniume-Server auf:
java -jar ./selenium-server-standalone-2.0.0.jar -debug -htmlSuite "*googlechrome /opt/google/chrome/chrome" http://wir.sind.dieb.org/ /jenkins/selenium/test-suite.html selenium.html -timeout 3600 -userExtensions ./selenium/user-extensions.js -avoidProxy -browserSessionReuse

Erläuterung
Achten Sie generell darauf, wohin Sie die Optionen und Parameter schreiben und wie. Teilweise bekommen Sie bei Fehlern keine Fehlermeldung, der Test läuft aber nicht wie erwartet oder funktioniert gar nicht.

  • “*googlechrome /opt/google/chrome/chrome” (bitte mit den neckischen “)
    sorgt dafür, dass der Browser direkt aufgerufen wird. Somit vermeiden wir nachstehende Fehlermeldung, die man nicht einfach ignorieren sollte, da andernfalls einige Events nicht korrekt erkannt werden:

Caution: ‘/usr/bin/firefox’: file is a script file, not a real executable. The browser environment is no longer fully under RC control

  • -timeout 3600
    Es gibt bei Selenium zwei verschiedene Arten von Timeouts: Der setTimeout-Befehl (respektive selenium.defaultTimeout in der Konfiguration) setzt die Zeit in ms fest, bis ein Kommando (waitFor, open) zu lange dauert und fehlschlägt.
    Die Startoptiontimeout, gibt wiederum in Sekunden vor, wie lange, der gesamte Testablauf dauern darf, bevor abgebrochen wird. 3600 Sekunden, das wäre eine Stunde, was inzwischen ernsthaft knapp wird. Falls Ihre Test Suite zu viel Zeit in Anspruch nimmt und abgebrochen wird, erhalten Sie zum einen lediglich eine leere Report-Datei, zum anderen eine Fehlermeldung der Art:

WARN – Google Chrome seems to have ended on its own.
HTML suite exception seen:
org.openqa.selenium.server.SeleniumCommandTimedOutException

  • -debug
    Sie bekommen einfach ein deutlich besseres Console-Protokoll in Jenkins angezeigt. Ansonsten sehen Sie zwischen Start und Ende von Selenium einfach keine Rückmeldung ob des Fortschritts.

xvfb Einstellungen

  • konfigurieren Sie den export-Bildschirm mit 24 bit – 16 oder gar 8 bit bereiten diverse Probleme. In meinem Fall habe ich die Optionen direkt in /etc/init.d/xvfb mit folgender Zeile verankert:
case "$1" in
 start)
 /usr/bin/Xvfb :99 -ac -screen 0 1280x1024x24 &
 ;;
  • Sollte es mit Xvfb Probleme geben bzw. Sie den start- oder export-Befehl vergessen haben, ist dies meist Ursache der folgenden Fehlermeldung:

java.lang.RuntimeException: Timed out waiting for profile to be created!

Entgegen häufiger Empfehlung brachte das an- sowie festlegen eines Firefox-Profils bei mir keine Abhilfe.

  • Ignorieren können Sie hingegen Meldungen von Xvfb wie

Could not init font path element /usr/share/fonts/X11/100dpi/:unscaled, removing from list!

Screenshots erstellen
Wer einen kurzen Blick in die Selenium-Dokumentation wirft, wird zwar schnell feststellen, dass Screenshots und html-Suite nur unter Verwendung des Firefox im Chrome-Modus vereinbar sind, allerdings bleibt unerwähnt, dass es unter Umständen auch dort Probleme bereitet. Exemplarisch ein Aufruf, wie er bei mir in Selenium IDE vorkommt:

captureEntirePageScreenshot
 //tmp/screen-001a.png
  • Lassen Sie die Screenshots im tmp-Ordner speichern, das verursacht gewöhnlich keine Probleme; abschließend lassen Sie Jenkins einfach die Bilder in den Workspace-Ordner verschieben, so haben Sie bequem Zugriff darauf.
  • Die Screenshot-Funktion schlägt bei „nicht ordentlichen“ html-Seiten fehl, also wenn diese beispielsweise nicht/fehlerhaft geladen wurde oder es sich um reinen Text oder formloses xml handelt.
  • Für etwas wie „Sceenshot on fail“ wäre mir bei html-Suites keine Möglichkeit bekannt. Machen Sie Screenshots daher immer am Anfang des nächsten Tests bzw. legen Sie einen extra Fall an, der dies nachträglich macht. Schlägt ein Test fehl, springt Selenium zum nächsten Testfall, der im Idealfall ein Bild vom Schlamassel anfertigt.
  • Ohnehin möchte ich hier nochmals daran erinnern, bestimmte Teile der Testfälle immer „umgekehrt“ anzuordnen, jedenfalls neigte ich bei meinen ersten Schritten dazu, sklavisch an dem sich ergebenden zeitlichen Ablauf zu hängen, was grundverkehrt ist. Es ist falsch, Fenster zu schließen oder sonstige Änderungen während – oder zum Abschluss eines Tests rückgängig zu machen. Schlägt dieser fehl, werden die nötigen Schritte natürlich nicht ausgeführt, man erhält keine Rückmeldung und schlimmstenfalls geraten nachfolgende Prüfungen ins Straucheln.

Build am Donnerstag: Jenkins Continuous Build Server für PHP-Projekte

Jenkins ist ein erweiterbares, webbasiertes System zur kontinuierlichen Integration in agilen Softwareprojekten.“ (Danke für diese praktische Einleitung, Wikipedia!).
Ich habe das schnieke Jenkins-System gerade für ein PHP-basiertes Projekt eingerichtet, und zwar mit Apache Ant als Build-Tool, Git übernimmt das Source-Code-Management. Winstone wird als Servlet-Container schon mitgeliefert, andere Programme wie etwa Tomcat sind möglich, aber nicht nötig.
Eine genaue Anleitungen zum Einrichten samt Template gibt’s hier: Jenkins Job Template for PHP-Projects. Die dort zu findende Vorlage liefert für den ersten Jenkins-PHP-Job die gängigsten Einstellungen der Post-Built-Aktionen, Graphen etc.
Hier sei angemerkt, dass zwar ein Jenkins-Template-Plugin als solches existiert, das es allerdings nur erlaubt, einen Teil der Optionen zu übertragen – praktisch gänzlich unnötig, lassen sich bestehende Job-Ordner doch einfach kopieren, umbenennen oder zu anderen Jenkins-Installationen verschieben (anschließend die Konfiguration von Jenkins neu laden).
Für PHP-Projekte sind folgende Jenkins-Plugins interessant:Ein paar der Graphen
Checkstyle (verarbeitet PHP_CodeSniffer-Logdateien im Checkstyle-Format)
Clover PHP (verarbeitet PHPUnit Code-Coverage xml-Ausgabe)
DRY (verarbeitet phpcpd-Logdateien im PMD-CPD-Format)
HTML Publisher (z.B. zum veröffentlichen des PHPUnit code coverage report)
JDepend (verarbeitet PHP_Depend-Logdateien im JDepend Format)
Plot (verarbeitet phploc CSV-Ausgabe)
PMD (verarbeitet PHPMD-Logdateien im PMD-Format)
Violations (verarbeitet diverse Logdateien)
xUnit (verarbeitet PHPUnit-Logdateien im JUnit format)
Automation
Für die Steuerung legt man ein Ant-Build-Skript an, sowie die Konfigurationen/Rulesets für die einzelnen Tools (etwa für CodeSniffer – siehe unseren Artikel). So lassen sich Builds individuell schneidern, von Code Coverage, Dupliziertem Quelltext, bis hin zur Verfügbarkeit der Datenbank.
In unserem Fall fungiert Git zudem als Build-Trigger und startet bei neuen Versionen im Repository den Job – inklusive Benachrichtigung der Git-User bei einem Fehlschlag (per E-Mail, Jabber u.A.) oder Git Publisher bei Erfolg. Die Jobs lassen sich alternativ manuell oder per Script starten, zeitlich Planen und Ruheperioden einrichten.

Distributed Builds

Eine Jenkins-Installation kann als Master agieren und Builds auf Nodes (bzw. Slaves) laufen lassen, auf ein und demselben Rechner oder externen Systemen, egal ob nun Windows oder anderen Unixen, etwa Solaris.
Eine Jenkins-Installation auf dem Slave ist nicht nötig – der Master kopiert die nötigen Dateien nach der Einrichtung. Wohl aber sollte der Node über alle für den Build erforderlichen Tools, etwa CVS, Pear, PHP und PHP-Tools (PHPUnit etwa schreit nach PHP ab 5.2.7) sowie die Konfigurationsdateien (sofern diese nicht im Repository liegen) verfügen. Einige Programme wie Maven, Ant, JDKs und Git installiert Jenkins auf Wunsch automatisch, wobei sich verschiedene Arten einstellen lassen.
Jobs legt man auf dem Master an und weist diese entweder dediziert einem dieser „Dumb“-Slaves zu (wer es sucht: nennt sich „Beschränke wo dieses Projekt ausgeführt werden darf“) oder es werden automatisch die vorhandenen Build-Prozessoren bei anstehenden Jobs ausgelastet. D.h. ein Job wird immer komplett ausgelagert, einzelne Test zu verteilen geht leider nicht. Die Ergebnisse bestaunt man auf dem Master.
Bei meinem Versuchsaufbau greift Jenkins unter Debian auf einen CentOS-Slave per ssh zu. Statt per ssh ließe sich ein Node auch „headless“ per Command-Line oder Skript sowie Java Web Start (JNLP) steuern.
Pferdefuß in meinem Fall: die unterschiedlichen PHP-Versionen. Einige PHPUnit-Test, die auf dem Master (mit PHP 5.3) funktionieren, laufen nicht auf dem vorgesehenen Slave. Ergo Tests umschreiben, oder für die Zukunft vorher überlegen, wo diese laufen sollen 😉