Selenium ist und bleibt Open Source

… und damit kostenlos. Simon Stewart, Software Engineer in Test bei Google und Chefentwickler von Selenium Webdriver (dem Nachfolger von Selenium RC) hat sich heute dazu bekannt, dass Selenium Open Source bleiben wird, so lange er das Sagen hat. Dies ist für den zukünftigen Erfolg des Selenium-Projekts eine zentrale Aussage. Nun gibt es in puncto Investitionssicherheit quasi eine langfristige Garantie für Unternehmen, die auf Selenium zur Testautomatisierung ihrer Internetanwendung setzen. Weiterhin dürfte diese Festlegung den Abstand zum Hauptkonkurrenten HP Unified Functional Testing (ehemals QTP, proprietär) vergrößern. Eine offizielle Ausweisung der Lizenzkosten seitens HP ist im Netz nicht zu finden. Hier finden Sie den kompletten Thread in Selenium Users: https://groups.google.com/forum/?fromgroups=#!topic/selenium-users/7zIYjJaVgOY

Email-Korrespondenz in Selenium integrieren

Das häufigste Testszenario, in dem Emails integriert sind, ist der Registrierungsvorgang bei einer Internet-Anwendung. Nachdem der neue User das Kontaktdatenformular ausgefüllt und abgeschickt hat, bekommt er eine Email in die Mailbox, welche ein Sicherheitsmerkmal (Link, Passwort) enthält, dessen Benutzung die Registrierung erfolgreich abschließt.
Nun ist der Einsatz von Selenium weitgehend auf die Reichweite des Browsers beschränkt, will sagen, dass man nicht ohne andere Tools und Aufwand auf Mailclients (Outlook, Thunderbird, …) bei der Automatisierung der Tests zurückgreifen kann. Es gibt jedoch die Möglichkeit sich eines Webmail-Clients zu bedienen, der eine Administrationsoberfläche für Emails darstellt, die vollständig innerhalb eines Browsers bedient werden kann. Den Ansatz über einen Instant-Email-Dienst (spambog.com, instant-mail.de, …) zu gehen, empfehle ich für den produktiven Testeinsatz nicht, da diese im Emailempfang mindestens verzögert, wenn nicht gar gänzlich unzuverlässig sind. So hatte beispielsweise spambog.de bei einem Einsatz eine Verzögerung von genau einer Stunde. Bei meinem aktuellen Auftrag: www.hiorg-server.de wird ausdrücklich von der Nutzung von den bekannten Mailprovidern (web.de, hotmail.de, …) abgeraten, da manche Funktionen des hiorg-server.de dort als Spam gewertet werden (natürlich ohne es zu sein), weshalb diese Möglichkeit auch wegfällt. Ich nehme mal an, dass die meisten Tester mindestens einen Webspace mit eigener Domain und angeschlossenem Webmail-Client besitzen. Dies ist für den Standardgebrauch in Selenium ausreichend. Ich benutze für diesen Artikel Atmail, weshalb auch meine WebMail-Klasse auf diesen WebMailer ausgerichtet ist.
Die Auslagerung der Emailfunktionen in eine separate Mail-Klasse entspricht der Anforderung des Page Object Patterns, dass man die Navigation beliebig umbauen kann, ohne jedoch die Testklassen selbst ändern zu müssen. Dieses Design schließt auch ein, dass der domain part der Email-Adresse (wiki) nicht in der Testklasse auftaucht. Diese Testklasse kenne nur die Methoden der Mail-Klasse, ohne ihre Implementierungsdetails, also in unserem Fall:
new WebMail().getRegistrationText(String keywordInSubject, boolean löscheEmail)
1.) Das keywordInSubject ist notwendig, um die Email meines Threads eindeutig in meiner Mailbox zu identifizieren. Bei paralleler Ausführung unterschiedlicher Testcases (mit Email-Beteiligung) kann man naturgemäß als Tester nicht die Reihenfolge vorausahnen, wann welcher Thread die Mailbox erreicht, um dort „seine“ Email abzuholen. Hier entsteht also eine Race-Condition zwischen den parallel getesteten Testcases. Logiken, wie „nimm die erste Email in der Mailbox“ scheiden daher aus. Wir benötigen eine Möglichkeit wie der Thread „seine“ Email erkennt, diese also eindeutig in der Mailbox unterscheidbar ist.
2.) mit boolean löscheEmail kann ich mich entscheiden, ob nach der Prüfung die Registriermail gelöscht werden kann, um unseren Stall sauber zu halten…
Und hier der Code:
EmailTest.java

package de.hiorgserver.testhiorgserver;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.Test;
public class EmailTest {
    @Test
    public void testEmail() throws MalformedURLException, InterruptedException {
        String username = "cloudbees_it-kosmopo";
        String accessKey = "XXX";
        String url = "http://" + username + ":" + accessKey + "@ondemand.saucelabs.com:80/wd/hub";
        DesiredCapabilities cap = DesiredCapabilities.firefox(); //Browserdefaults auswählen
        cap.setCapability("version", "15"); //Version des Browsers festlegen
        cap.setCapability("platform", "Windows 2008"); //Betriebssystem festlegen
        cap.setCapability("username", username); //Mein Username bei SauceLabs
        cap.setCapability("accessKey", accessKey); //Mein Access-key bei SauceLabs
        WebDriver driver = new RemoteWebDriver(new URL(url),cap);
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        WebMail webmailer = new WebMail(driver, "test");
        String emailText = webmailer.getRegistrationText("mein Blog", true);
        Assert.assertTrue(emailText.contains("Benutzerkonto"), "Prüfung, ob der Registrierungstext das Schlüsselwort 'Benutzerkono' enthält.");
        driver.quit();
    }
}

WebMail.java

package de.hiorgserver.testhiorgserver;
import java.util.List;
import java.util.Set;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.interactions.Actions;
public class WebMail {
    WebDriver driver;
    String mainWindow;
    WebMail (WebDriver driver,String localPart) {
        this.driver = driver;
        String url = "http://webmail.it-kosmopolit.de/";
        String password = "XXX";
        //Öffne ein neues Fenster, dann kann ich es zwischendurch immer wieder verwenden.
        mainWindow = driver.getWindowHandle();
        JavascriptExecutor js = (JavascriptExecutor) driver;
        js.executeScript("window.open('','Email-Fenster')");
        driver.switchTo().window("Email-Fenster");
        driver.get(url);
        driver.switchTo().frame("GroupingFrame");
        driver.findElement(By.id("username")).sendKeys(localPart);
        driver.findElement(By.id("password")).sendKeys(password);
        driver.findElement(By.name("Submit")).click();
    }
    String getRegistrationText(String keywordInSubject, boolean löscheEmail) throws InterruptedException {
        if ( ! driver.getCurrentUrl().contains("http://webmailtest.it-kosmopolit.de")) driver.switchTo().window("Email-Fenster") ;
        Thread.sleep(2000); //nur für's Schauvideo angehalten.
        Actions action = new Actions(driver);
        action.doubleClick(driver.findElement(By.xpath("//*[contains(text(),'" + keywordInSubject + "')]")));
        action.perform();
        Thread.sleep(2000); //nur für's Schauvideo angehalten.
        driver.switchTo().frame("msgwindow1");
        String link = driver.findElement(By.xpath("//*[contains(text(),'Registrierung')]")).getText();
        if (löscheEmail) {
            driver.switchTo().defaultContent();
            driver.findElement(By.id("Folderdelete")).click();
        }
        Thread.sleep(2000); //nur für's Schauvideo angehalten.
        driver.get(link);
        Thread.sleep(2000); //nur für's Schauvideo angehalten.
        String text = driver.findElement(By.tagName("body")).getText();
        driver.switchTo().window(mainWindow);
        return text;
    }
}

Und hier der Testverlauf samt Video: https://saucelabs.com/tests/5e80dba5bfe74327aa68aaaefaa08737#
Hinweis: der Test würde noch schneller laufen, wenn ich nicht den Thread mehrmals schlafen gelegt hätte, damit der Programmverlauf im Video leichter nachvollzogen werden kann.
„Verschärfte“ Testbedingungen
Die Internetanwendung kann erzwingen, dass die Email-Adressen innerhalb der Internet-Anwendung eindeutig sein müssen. Dies ist beispielsweise dann der Fall, wenn die Email gleichzeitig die Bezeichnung des Users ist. Wenn ich nun gegen eine Testmaschine teste, habe ich bei einer hartcodierten Emailadresse nur einen Test frei. Jeden wiederholten/parallelen Testdurchlauf wird mir die Testumgebung quittieren mit: „Ihre Email-Adresse ist bereits vergeben“. Dann müsste theoretisch jedes Mal die Datenbasis der Testumgebung auf den „Zeitpunkt 0“ zurückgestellt werden – ein hoher manueller Aufwand!
Einen Lösungsansatz für diese verschärften Testbedingungen möchte ich an dieser Stelle nur mal skizzieren:

  1. Neuen Webspace incl. Domain erstellen
  2. als local part (wiki) einen Timestamp (natürlich nur erlaubte Zeichen) nehmen. Durch den (Millisekunden-genauen) Timestamp ist mit hoher Wahrscheinlichkeit die verwendete Email-Adresse unique. Anhand des Empfängers kann man die Emails im Webmail-Client also eindeutig identifizieren.
  3. Forward mail of non-existent user to following-default Emailadresse: test@neuedomain.de
  4. test@neuedomain.de per Webmail-client aufrufen

Selenium WebDriver MindMap


This draft of a Selenium WebDriver MindMap was drawn with the wonderful https://bubbl.us/. The goal of this draft is to propose an overview of the main subjects while working with Selenium WebDriver and where it make sense to acquire knowledge sooner or later.
Integration
WebDriver is developed for the real world and therefore it depends on real projects with real setups. So a Selenium developer needs knowledge of these environments.
Agile Development
Web Browser Automation is an important part of agile development http://en.wikipedia.org/wiki/Agile_software_development
Cloud
www.saucelabs.com, www.cloudbees.com, …
Build-Tools
Maven, Ant, …
Continous Integration
The shorter the test cycles, the more Web Browser Automation pay off. CI-Server: Jenkins
WebDriver API
It’s the core of the Selenium engagement: http://selenium.googlecode.com/svn/trunk/docs/api/java/index.html
Selenium World
Knowing the history (Selenium 1 -> Selenium 2 = WebDriver), knowing the key players (e.g. Simon Stewart), knowing the knowledge base (http://seleniumhq.org/), knowing support ressources (https://groups.google.com/forum/?fromgroups#!forum/selenium-users), …
Pattern
There are design patterns, like Page Object-Pattern.
And there are recipes for recurrend problems, like „check if an element exists“ (http://stackoverflow.com/questions/6521270/webdriver-check-if-an-element-exists)
Internals
Knowing the internals isn’t needed in straight forward problems. But it’s very helpful with complex problems and to develop sophisticated solutions.
Driver Internals
http://code.google.com/p/selenium/wiki/ArchitecturalOverview#A_Layered_Design
Browser Internals
Long time the rendering engines of browsers have been black boxes. Tali Garsiel helped a lot to reveal these internals: http://taligarsiel.com/Projects/howbrowserswork1.htm
Programming
Except for stumbeling with Selenium IDE, skills in programming are fundamental for Web Browser Automation with Selenium.
Programming Language
All programming languages with bindings to WebDriver API: http://seleniumhq.org/docs/03_webdriver.html#setting-up-a-selenium-webdriver-project
IDE
NetBeans, Eclipse, …
HTML
target of all the efforts
XPath
needed for sophisticated navigation; fantastic tutorial:
Test-Framework
TestNG, junit, …
Browser Tools
in the MindMap you find addons for my favorite development browser: Firefox.

Anpacken!