Selenium: Parallelisierung durch Parametrisierung

Im Blogbeitrag Parallelisierung von Selenium-Tests mittels Sauce Labs blieb ich noch eine skalierbare Parallelisierungslösung schuldig. TestNG bietet mit seinem DataProvider die Möglichkeit Parameter an seine Testmethoden zu übergeben (http://testng.org/doc/documentation-main.html#parameters-dataproviders). Dieses Tool möchte ich nun nutzen, um meinen Testmethoden zu sagen, welche Browser sie für meine Seleniumtests verwenden sollen. Als „Browserpark“ nutze ich wieder mal die Cloud der Sauce Labs Inc.
Den DataProvider kann man auch außerhalb der Testklasse speichern und dann in jede Testklasse integrieren. Meist sind bei den Funktionstests zahlreiche Testklassen im Spiel. Da ich nicht bei jedem major release von Firefox und Konsorten in jede Testklasse einzeln gehen möchte, um diesen dort hinzuzufügen, drängt sich Auslagerung des DataProviders in eine eigene .class geradezu auf.
Die verwendeten Browser sind:

  1. Firefox, Version 12, Linux
  2. Internet Explorer, Version 9, Windows Vista
  3. Chrome, Version von Sauce Labs bestimmt, Windows Vista
  4. Safari, Version 5, Mac

Ich verwende im Beispiel also zwei Testklassen namens Parallel1Test.java und Parallel2Test.java, sowie eine Klasse worin ich den DataProvider auslagere, die ich, angelehnt an einen Fuhrpark, mit BrowserPark.java bezeichne.
Parallel1Test.java

import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class Parallel1Test {
    @Test(description = "Testet Erreichbarkeit meiner Angebotsseite.", dataProvider = "BrowserPark", dataProviderClass = BrowserPark.class)
    public void paralleltest1(URL url, DesiredCapabilities caps) throws MalformedURLException, InterruptedException {
        //Konfigurationen
        caps.setCapability("name", "paralleltest1: check Michael Wowro");
        WebDriver driver;
        if (BrowserPark.isDevelopedLocal) driver = new FirefoxDriver(caps);
        else driver = new RemoteWebDriver(url, caps);
        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
        //Testcase
        driver.get("https://www.it-kosmopolit.de");
        driver.findElement(By.linkText("KONTAKT UND ANGEBOT")).click();
        WebElement profil = driver.findElement(By.partialLinkText("xing"));
        Assert.assertTrue(profil.getText().contains("Michael_Wowro"));
        //Beenden
        driver.quit();
    }
}

Parallel2Test.java

import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class Parallel2Test {
    @Test(description = "Testet Erreichbarkeit meiner Angebotsseite.", dataProvider = "BrowserPark", dataProviderClass = BrowserPark.class)
    public void paralleltest2(URL url, DesiredCapabilities caps) throws MalformedURLException, InterruptedException {
        //KonfigurationeN
        caps.setCapability("name", "paralleltest2: check xing");
        WebDriver driver;
        if (BrowserPark.isDevelopedLocal) driver = new FirefoxDriver(caps);
        else driver = new RemoteWebDriver(url, caps);
        driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
        //Testcase
        driver.get("https://www.it-kosmopolit.de");
        driver.findElement(By.linkText("KONTAKT UND ANGEBOT")).click();
        WebElement profil = driver.findElement(By.partialLinkText("xing"));
        Assert.assertTrue(profil.getText().contains("xing"));
        //Beenden
        driver.quit();
    }
}

browserPark.java

import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.DataProvider;
// Übersicht über Browser-Angebot bei Sauce Labs: https://saucelabs.com/docs/browsers
public class BrowserPark {
    static boolean isDevelopedLocal;
    static String username = "it-kosmopolit";
    static String accessKey = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
    static String url = "http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub";
//    /* Entwicklung gegen meinen lokalen Firefox-Browser */
//    @DataProvider(name = "BrowserPark")
//    public static Object[][] developLocal() throws MalformedURLException {
//        isDevelopedLocal = true;
//        DesiredCapabilities capFF = DesiredCapabilities.firefox(); //Browserdefaults auswählen
//
//        return new Object[][]{
//                    {new URL("https://www.egal.de"), capFF}
//        };
//    }
//    /* Entwicklung gegen einen ausgesuchten Browser in der Sauce Labs-Cloud */
//    @DataProvider(name = "BrowserPark")
//    public static Object[][] developRemote() throws MalformedURLException {
//        isDevelopedLocal = false;
//
//        // FF
//        DesiredCapabilities capFF = DesiredCapabilities.firefox(); //Browserdefaults auswählen
//        capFF.setCapability("version", "12"); //Version des Browsers festlegen
//        capFF.setCapability("platform", "Linux"); //Betriebssystem festlegen
//        capFF.setCapability("username", username); //Mein Username bei SauceLabs
//        capFF.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLabs
//
//        return new Object[][]{
//                    {new URL(url), capFF}
//        };
//    }
    /* Produktivmodus mit den von meinen Kunden gewünschten Browser-Betriebssystem Kombinationen */
    @DataProvider(name = "BrowserPark")
    public static Object[][] productive() throws MalformedURLException {
        isDevelopedLocal = false;
        // FF
        DesiredCapabilities capFF = DesiredCapabilities.firefox(); //Browserdefaults auswählen
        capFF.setCapability("version", "12"); //Version des Browsers festlegen
        capFF.setCapability("platform", "Linux"); //Betriebssystem festlegen
        capFF.setCapability("username", username); //Mein Username bei SauceLabs
        capFF.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLabs
        // IE
        DesiredCapabilities capIE = DesiredCapabilities.internetExplorer(); //Browserdefaults auswählen
        capIE.setCapability("version", "9"); //Version des Browsers festlegen
        capIE.setCapability("platform", "Windows 2008"); //Betriebssystem festlegen
        capIE.setCapability("username", username); //Mein Username bei SauceLabs
        capIE.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLab
        //Chrome
        DesiredCapabilities capCH = DesiredCapabilities.chrome(); //Browserdefaults auswählen
        capCH.setCapability("platform", "Windows 2008"); //Betriebssystem festlegen
        capCH.setCapability("username", username); //Mein Username bei SauceLabs
        capCH.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLab
        //Safari
        DesiredCapabilities capSA = DesiredCapabilities.safari(); //Browserdefaults auswählen
        capSA.setCapability("version", "5");//Version des Browsers festlegen
        capSA.setCapability("platform", "Mac 10.6");//Betriebssystem festlegen
        capSA.setCapability("username", username); //Mein Username bei SauceLabs
        capSA.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLab
        return new Object[][]{
                    {new URL (url), capFF},
                    {new URL (url), capIE},
                    {new URL (url), capCH},
                    {new URL (url), capSA}
        };
    }
}

Wie im Code zu sehen, habe ich drei Versionen des DataProviders:

  1. zum Entwickeln der Tests gegen meinen aktuell lokal installierten Firefox
  2. zum Entwickeln der Tests gegen einen ausgewählten Browser in der Sauce Labs-Cloud
  3. und den vom Kunden gewünschten Browserpark, gegen den seine Internet-Anwendung getestet wird (sozusagen der Produktivmodus). Und hier kann man sich die Browser-Betriebssystem-Kombinationen für seinen individuellen Browserpark zusammenstellen: https://saucelabs.com/docs/browsers

Die beiden Versionen, die ich gerade nicht nutzen möchte, schalte ich einfach per Auskommentieren aus.
Alle 3 Klassen liegen in meiner Maven-Verzeichnisstruktur \src\test\java. Ein entsprechendes Maven-Projekt habe ich in netbeans angelegt. Ein Click auf „test“ und nachdem mir netbeans den erfolgreichen Testlauf bestätigt, gehe ich in mein Sauce Labs-Konto um mir die Schätzchen anzuschauen. Und siehe da, für jeden meiner vier verschiedenen Browser sind je beide Tests erfolgreich durchgelaufen:

Hinweis: Da das für diesen Blogpost verwendete Sauce Labs-Konto auf zwei Threads beschränkt ist, sind in diesem Beispiel immer nur zwei Testläufe parallel.
Weil ich es richtig gut finde, dass Sauce Labs von jedem meiner Testläufe ein Video aufzeichnet, schau ich mir eins auch noch mal an: