Parallelisierung von Selenium-Tests mittels Sauce Labs

Quickstart
Voraussetzungen
– lauffähiges Maven: http://maven.apache.org
– Account bei SauceLabs: https://saucelabs.com/
1.) irgendwo den Ordner it-kosmopolit anlegen, darunter den Ordner src, darunter den Ordner test, darunter den Ordner java
2.) ParallelTest.java in it-kosmopolit\src\test\java legen (credentials im code anpassen, siehe unten)

package de.itkosmopolit.blog;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.annotations.Test;
import org.testng.Assert;
import java.net.URL;
import java.net.MalformedURLException;
public class ParallelTest {
    @Test
    public void parallel_with_firefox() throws MalformedURLException{
		//Konfigurationen
		DesiredCapabilities caps = DesiredCapabilities.firefox(); //Browser auswählen
        caps.setCapability("version", "13"); //Version des Browsers festlegen
        caps.setCapability("platform", "Linux"); //Betriebssystem festlegen
		caps.setCapability("name", "parallelTestFirefox"); //hier wird der Test benannt und in SauceLabs wieder auffindbar.
		caps.setCapability("username", "it-kosmopolit"); //Mein Username bei SauceLabs
		caps.setCapability("accessKey", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); //Mein Access-key bei SauceLabs
        RemoteWebDriver driver = new RemoteWebDriver(
                new URL("http://it-kosmopolit:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@ondemand.saucelabs.com:80/wd/hub"),
                caps);
		//Testcase
        driver.get("https://www.it-kosmopolit.de");
        driver.findElementByLinkText("KONTAKT UND ANGEBOT").click();
		WebElement profil = driver.findElement(By.partialLinkText("xing"));
		Assert.assertTrue(profil.getText().contains("Michael_Wowro"));
		//Beenden
        driver.quit();
    }
	 @Test
    public void parallel_with_internetExplorer() throws MalformedURLException {
	    //Konfigurationen
		DesiredCapabilities caps = DesiredCapabilities.internetExplorer(); //Browser auswählen
        caps.setCapability("version", "9"); //Version des Browsers festlegen
        caps.setCapability("platform", "Windows 2008"); //Betriebssystem festlegen
		caps.setCapability("name", "parallelTestInternetExplorer"); //hier wird der Test benannt und in SauceLabs wieder auffindbar.
		caps.setCapability("username", "it-kosmopolit"); //Mein Username bei SauceLabs
		caps.setCapability("accessKey", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); //Mein Access-key bei SauceLabs
        RemoteWebDriver driver = new RemoteWebDriver(
                new URL("http://it-kosmopolit:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@ondemand.saucelabs.com:80/wd/hub"),
                caps);
		//Testcase
        driver.get("https://www.it-kosmopolit.de");
        driver.findElementByLinkText("KONTAKT UND ANGEBOT").click();
		WebElement profil = driver.findElement(By.partialLinkText("xing"));
		Assert.assertTrue(profil.getText().contains("Michael_Wowro"));
		//Beenden
        driver.quit();
    }
}

3.) pom.xml in it-kosmopolit legen

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.itkosmopolit.blog</groupId>
    <artifactId>paralleltest</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>5.13.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium</artifactId>
            <version>2.0b1</version>
        </dependency>
	</dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
					<parallel>methods</parallel>
					<threadCount>10</threadCount>
                </configuration>
                <executions>
                    <execution>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <configuration>
                            <skip>false</skip>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.5</source>
                    <target>1.5</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.) in der DOS-Box in it-kosmopolit gehen und Folgendes ausführen: C:\”Program Files”\apache-maven-3.0.x\bin\mvn integration-test
Testcase
1.) Geh auf meinen Blog http://itkosmopolit.wordpress.com/
2.) Klicke dort auf den Link “KONTAKT UND ANGEBOT”
3.) Wenn der Link zum XING-Profil den Linktext “Michael_Wowro” enthält, dann OK.
Story
Gerade wenn man die Funktionstests in seinen Continuous Integration einbinden will, jedoch auch, wenn man diese als Regressionstests vor jedem Release nutzt, kommt es auf die Durchführungsgeschwindigkeit der Tests an. Das Konzept schlechthin, die Funktionstests zu beschleunigen ist die Parallelisierung. Ich wähle für diesen Blogbeitrag Mozilla Firefox (auf Linux) und Internet Explorer (auf Windows) als Selenium Browser aus. Als Testframeworks werden in der Selenium-Community sowohl JUnit, als auch TestNG verwendet. TestNG erfreut sich jedoch größerer Beliebtheit (http://sauceio.com/index.php/2010/10/parallel-junit-4-and-selenium-part-one-parameters), weshalb ich es im Folgenden verwende. Für die Parallelisierung auf Methodenebene sorgt Maven und benötigt hierfür nur zwei Zeilen (siehe auch http://maven.apache.org/plugins/maven-surefire-plugin/examples/testng.html):

Das gleichzeitige Ausführen eines Selenium-Tests auf zwei verschiedenen Browsern (Parallelisierung) auf einem Rechner erweist sich als instabil. Manchmal funktioniert’s, manchmal gibt’s ein Unable to find element:

Diese Diskussion bestätigt dieses Phänomen: http://www.seleniumwebdriver.com/selenium-webdriver-developers/webdriver-directed-firefox-instances-fighting-each-other-on-the-same-machine/
Da ich mir das Aufsetzen eines Selenium Grids sparen möchte, gehe ich gleich zur Cloud-Lösung = Sauce Labs über: http://sauceio.com/index.php/tag/parallel-testing/ Voraussetzung ist hierfür natürlich ein Konto bei SauceLabs, welches jedoch einfach und ohne Verzögerung anzulegen ist. Für die Nutzung der SauceLabs API durch unser Testprogramm sind lediglich ein Username (wählt man selbst bei Anlegen des Kontos) und ein API-key (synonym: Access-key) nötig. Letzteren erzeugt SauceLabs automatisch und man kann diesen im Konto einsehen (und dort auch neu generieren lassen):
Dem besseren Verständnis geschuldet, baue ich das Testprogramm ohne Parametrisierung, also mit je einer eigenen Testmethode für den Firefox und einer für den Internet Explorer.
Die von SauceLabs angebotenen Browser-Betriebssystem-Kombis findet man hier: http://saucelabs.com/docs/ondemand/browsers – dort kann man auch direkt den Code für die gewünschte Programmiersprache generieren lassen. In diesem Blogbeitrag wird Java verwendet.
An dieser Stelle ist die SauceLabs-Doku etwas schlampig und die Eingabe der Credentials redundant. Neben der Verwendung des Usernames und des Access-Keys in der url, muss man diese auch noch in den capabilities aufrufen:

capabillities.setCapability("username", "it-kosmopolit");
capabillities.setCapability("accessKey", "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
RemoteWebDriver driver = new RemoteWebDriver(
new URL("http://it-kosmopolit:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"@ondemand.saucelabs.com:80/wd/hub]]"),
capabillities);

Beachtet man dies nicht, wird man mit folgender Fehlermeldung “belohnt”:

"java.lang.UnsupportedOperationException: Unknown username.
You sent username 'None' in your browser string, which is not a valid Sauce Labs account."

Siehe auch: http://saucelabs.com/forums/viewtopic.php?id=509
Wenn alles gut geht, dann erscheint in der DOS-Box die Meldung mit Failures: 0 und Errors: 0

Unter myTests im SauceLab-Account kann man sich die (von mir erzeugten) Testergebnisse als Video und Log anschauen

Wenn man sich nun die Endzeiten (Finished) und die Laufzeit (Duration) anschaut, erkennt man die Parallelität der Testausführung. q.e.d.
Die Testreports habe ich auf public gesetzt – daher kann nun jeder, der die url kennt (=Datensicherheit) sich diese nun anschauen:
Internet Explorer
Firefox
Die Videos sind bedingt durch die Einfachheit der Testcases natürlich nicht besonders spektakulär – die Möglichkeiten, die damit gezeigt werden jedoch schon …
Spätestens, wenn man sich die Übersichtlichkeit der Testreports anschaut, erkennt man, dass die Jungs von SauceLabs einen tollen Job machen.

Parametrisierte Google Suche

Motivation
Ein wichtiges Instrument eines Webscrapers sind tatsächlich URLs, denn damit landet man immerhin schon mal auf der gewünschten Seite (manchmal auch schon in den gewünschten Ergebnissen). Im Kontext der Google Suche kann die gezielte Verwendung von URLs auch für einen Otto-Normal-Nutzer eine Arbeitserleichterung sein. Hat man regelmäßige gleichlautende Abfragen, speichert man diese sinnvollerweise als URL in seinen Favoriten/Bookmarks/Lesezeichen.
Anatomie der Google Suche URL
die URL beginnt immer mit
https://www.google.com/search?
dahinter werden die Parameter in beliebiger Reihenfolge angehängt
parametername=parameterwert
mehrere Parameter werden mit & zusammengefügt, sodass die URL letzendlich diese Form annimmt
https://www.google.com/search?parametername1=parameterwert1¶metername2=parameterwert2¶metername3=parameterwert3
Die Parameter
Da Google keine offizielle Spezifikation seiner Parameter rausgibt, muss man sich diese von verschiedenen Seiten zusammenklauben (bzw. selbst herausfinden):
Google Search URL Parameters – Query String Anatomy
The Ultimate Guide to the Google Search Parameters
Google search parameters in 2012
Eine kleine vereinfachte Tabelle für unser u.g. Beispiel

Parameter Bedeutung
q=suchbegriff1+suchbegriff2 Das sind die Suchbegriffe, die in das google-Suchfeld eingetragen werden. Die einzelnen Suchbegriffe werden mit + aneinandergefügt.
as_eq=suchbegriff3+suchbegriff4 Das sind die Suchbegriffe, die von der Google Suche ausgeschlossen werden. Die einzelnen Suchbegriffe werden mit + aneinandergefügt.
tbs=qdr:d tbs=qdr filtert die Ergebnisse in einem bestimmten Zeitraum. d bedeutet hier Tag, d.h. nur die Suchergebnisse der letzten 24 h werden angezeigt (w bedeutet week usw.). Gerade wenn man regelmäßige Suchanfragen hat, möchte man ja nicht immer das Gleiche angezeigt bekommen, sondern nur die neuesten (noch nicht gelesenen) Suchergebnisse. Für solche Zwecke ist dieser Parameter natürlich Gold wert.
tbs=qdr:d,sbd:1 Wenn man dann noch die Ergenisse nicht nach Relevanz (ein Kriterium, das bei kleinen Zeiträumen eh keine Rolle spielt), sondern nach Zeit sortiert haben mag, hängt man noch ,sdb:1 an.
num=100&as_qdr=all num bedeutet die Anzahl der Suchergebnisse pro Seite. Seitdem Google jedoch Google Instant eingeführt hat, funktioniert dieser Parameter alleine nicht mehr. Man muss zusätzlich mittels as_qdr=all Google Instant ausschalten. Ein ausführlicher Artikel hierzu: num Parameter trotz Instant

Ein Beispiel
Als Beispiel nehme ich die Suchbegriffe “Google” und “Suche” – vielleicht wird dieser Artikel dereinst auf der ersten SERP stehen … naja … als auszuschließende Begriffe nehme ich willkürlich “Wikipedia” und “Apple” – im Suchfeld erreicht man den Ausschluss durch vorangestelltes Minuszeichen.

Die Google Suche URL sieht dann entsprechend so aus (kopiert die URL in Euer Browser-Adressfeld und seht die Magie …)
https://www.google.com/search?q=Google+Suche&as_eq=Wikipedia+Apple&tbs=qdr:d,sbd:1&num=100&as_qdr=all
Und dann wird diese URL schließlich noch als Favorit/Bookmark/Lesezeichen abgespeichert – feddisch.