Kapitel 4. Browser-gnostische Funktionen

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

In diesem Kapitel werden die Funktionen von Selenium WebDriver besprochen, die in verschiedenen Webbrowsern interoperabel sind. Ein wichtiges Mehrzweckmerkmal in dieser Gruppe ist die Ausführung von JavaScript. Außerdem ermöglicht die Selenium WebDriver API die Konfiguration von Timeouts für das Laden von Seiten und Skripten. Eine weitere praktische Funktion ist das Erstellen von Screenshots des Browserbildschirms oder nur des Teils, der einem bestimmten Element entspricht. Dann können wir mit WebDriver verschiedene Aspekte des kontrollierten Browsers verwalten, z. B. die Browsergröße und -position, den Verlauf oder die Cookies. Dann stellt WebDriver verschiedene Assets für die Steuerung bestimmter Webelemente zur Verfügung, z. B. Dropdown-Listen (d. h. HTML-Auswahlfelder und Datenlisten), Navigationsziele (d. h. Fenster, Tabs, Frames und Iframes) oder Dialogfelder (d. h. Warnungen, Eingabeaufforderungen, Bestätigungen und modale Dialoge). Zum Schluss erfährst du, wie du lokale und Sitzungsdaten mit Hilfe von Webspeichern verwaltest, Event-Listener implementierst und die Ausnahmen der Selenium WebDriver API nutzt.

Ausführen von JavaScript

JavaScript ist eine High-Level-Programmiersprache, die von allen wichtigen Browsern unterstützt wird. Wir können JavaScript auf der Client-Seite von Webanwendungen für eine Vielzahl von Operationen verwenden, z. B. für DOM-Manipulationen, Benutzerinteraktionen, die Verarbeitung von Anfragen und Antworten von Remote-Servern oder die Arbeit mit regulären Ausdrücken, neben vielen anderen Funktionen. Zum Glück für die Testautomatisierung ermöglicht Selenium WebDriver das Einfügen und Ausführen beliebiger JavaScript-Stücke. Zu diesem Zweck bietet die Selenium WebDriver API die Schnittstelle JavascriptExecutor. Tabelle 4-1 stellt die verfügbaren öffentlichen Methoden in dieser Schnittstelle vor, die in drei Kategorien unterteilt sind: synchrone, angeheftete und asynchrone Skripte. Die folgenden Unterabschnitte enthalten weitere Details und veranschaulichen ihre Verwendung anhand verschiedener Beispiele.

Tabelle 4-1. JavascriptExecutor Methoden
Kategorie Methode Rückgabe Beschreibung

Synchrone Skripte

executeScript(
    String script,
    Object... args)
Object

Führt JavaScript Code auf der aktuellen Seite aus.

Angepinnte Skripte

pin(String
    script)
ScriptKey

Füge einen Teil von JavaScript an eine WebDriver-Sitzung an. Die angehefteten Skripte können mehrfach verwendet werden, solange die WebDriver-Sitzung aktiv ist.

unpin(ScriptKey
    key)
void

Löse ein zuvor angeheftetes Skript von der WebDriver-Sitzung.

getPinnedScripts()
Set<ScriptKey>

Sammle alle angehefteten Skripte (jedes einzelne wird durch eine eindeutige ScriptKey identifiziert).

executeScript(
    ScriptKey key,
    Object... args)
Object

Rufe ein zuvor angeheftetes Skript auf (gekennzeichnet durch ScriptKey).

Asynchrone Skripte

executeAsyncScript(
    String script,
    Object... args)
Object

Führt JavaScript-Code (normalerweise eine asynchrone Operation) auf der aktuellen Seite aus. Der Unterschied zu executeScript() ist, dass Skripte, die mit executeAsyncScript() ausgeführt werden, ihre Beendigung explizit signalisieren müssen, indem sie eine Callback-Funktion aufrufen. Dieser Callback wird in der Regel als letztes Argument in das Skript eingefügt.

Jedes Treiberobjekt , das von der Klasse RemoteWebDriver erbt, implementiert auch die Schnittstelle JavascriptExecutor. Daher können wir , wenn wir einen Hauptbrowser (z. B. ChromeDriver, FirefoxDriver, usw.) verwenden, der über die generische Schnittstelle WebDriver deklariert wurde, in JavascriptExecutor umwandeln, wie im folgenden Ausschnitt gezeigt. Dann können wir den Executor (im Beispiel die Variable js ) verwenden, um die in Tabelle 4-1 aufgeführten Methoden aufzurufen.

WebDriver driver = new ChromeDriver();
JavascriptExecutor js = (JavascriptExecutor) driver;

Synchrone Skripte

Die Methode executeScript() eines JavascriptExecutor Objekts ermöglicht das Ausführen eines Stücks JavaScript im Kontext der aktuellen Webseite in einer WebDriver-Sitzung. Der Aufruf dieser Methode (in Java) blockiert den Kontrollfluss, bis das Skript beendet ist. Daher verwenden wir diese Methode in der Regel für die Ausführung von synchronen Skripten in einer zu testenden Webseite. Die Methode executeScript() erlaubt zwei Argumente:

String script

Obligatorisch JavaScript-Fragment, das ausgeführt werden soll. Dieser Code wird im Body der aktuellen Seite als anonyme Funktion (d. h. eine JavaScript-Funktion ohne Namen) ausgeführt.

Object... args

Optionales Argumente Skript. Diese Argumente müssen einem der folgenden Typen entsprechen: Zahl, Boolescher Wert, String, WebElement oder eine List dieser Typen (andernfalls löst WebDriver eine Ausnahme aus). Diese Argumente sind im injizierten Skript über die integrierte JavaScript-Variable arguments verfügbar.

Wenn das Skript einen Wert zurückgibt (d.h. der Code enthält eine return Anweisung), gibt die Selenium WebDriver executeScript() Methode auch einen Wert in Java zurück (andernfalls gibt executeScript() null zurück). Die möglichen Rückgabetypen sind:

WebElement

Bei der Rückgabe eines HTML-Elements

Double

Für Dezimalen

Long

Für nicht dezimale Zahlen

Boolean

Für boolesche Werte

List<Object>

Für Arrays

Map<String, Object>

Für Schlüssel-Wert-Sammlungen

String

Für alle anderen Fälle

Die Situationen, die die Ausführung von JavaScript mit Selenium WebDriver erfordern, sind sehr heterogen. In den folgenden Unterabschnitten werden zwei Fälle beschrieben, in denen der Selenium WebDriver keine eingebauten Funktionen bietet und wir stattdessen JavaScript verwenden müssen, um sie zu automatisieren: das Scrollen einer Webseite und die Handhabung eines Farbwählers in einem Webformular.

Scrollen

Wie in Kapitel 3 erläutert, ermöglicht Selenium WebDriver das Imitieren verschiedener Mausaktionen, darunter Klick, Rechtsklick oder Doppelklick. Allerdings ist es mit der Selenium WebDriver API nicht möglich, auf einer Webseite nach unten oder oben zu scrollen. Stattdessen können wir diese Automatisierung ganz einfach durch das Ausführen einer einfachen JavaScript-Zeile erreichen. Beispiel 4-1 zeigt ein einfaches Beispiel mit einer Übungswebseite (die URL dieser Seite steht in der ersten Zeile der Testmethode).

Beispiel 4-1. Test zur Ausführung von JavaScript, um einen Pixel nach unten zu scrollen
@Test
void testScrollBy() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/long-page.html"); 1
    JavascriptExecutor js = (JavascriptExecutor) driver; 2

    String script = "window.scrollBy(0, 1000);";
    js.executeScript(script); 3
}
1

Öffne eine Praxisseite mit sehr langem Text (siehe Abbildung 4-1).

2

Übertrage das Objekt driver auf JavascriptExecutor. Wir werden die Variable js verwenden, um JavaScript im Browser auszuführen.

3

Führe einen Teil des JavaScript-Codes aus. In diesem Fall rufen wir die JavaScript-Funktion scrollBy() auf, um das Dokument um einen bestimmten Betrag zu scrollen (in diesem Fall um 1.000 px nach unten). Beachte, dass dieses Fragment nicht return verwendet und wir daher kein Objekt in der Java-Logik zurückbekommen. Außerdem übergeben wir dem Skript kein Argument.

hosw 0401
Abbildung 4-1. Übungswebseite mit langem Inhalt

Beispiel 4-2 zeigt einen weiteren Test mit Scrollen und der gleichen Beispiel-Webseite wie zuvor. Anstatt eine feste Anzahl von Pixeln zu verschieben, bewegen wir das Dokument bis zum letzten Absatz der Webseite.

Beispiel 4-2. Test zum Ausführen von JavaScript, um zu einem bestimmten Element nach unten zu scrollen
@Test
void testScrollIntoView() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/long-page.html");
    JavascriptExecutor js = (JavascriptExecutor) driver;
    driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); 1

    WebElement lastElememt = driver
            .findElement(By.cssSelector("p:last-child")); 2
    String script = "arguments[0].scrollIntoView();"; 3
    js.executeScript(script, lastElememt); 4
}
1

Um diesen Test robust zu machen, geben wir ein implizites Timeout an. Andernfalls könnte der Test fehlschlagen, wenn die Seite bei der Ausführung der nachfolgenden Befehle noch nicht vollständig geladen ist.

2

Wir suchen den letzten Absatz auf der Webseite mithilfe eines CSS-Selektors.

3

Wir definieren das Skript, das in die Seite eingefügt werden soll. Beachte, dass das Skript keinen Wert zurückgibt, sondern als Neuerung das erste Funktionsargument verwendet, um die JavaScript-Funktion scrollIntoView() aufzurufen.

4

Wir führen das vorherige Skript aus und übergeben das gefundene WebElement als Argument. Dieses Element wird das erste Argument für das Skript sein (d.h. arguments[0]).

Das letzte Beispiel für das Scrollen ist das unendliche Scrollen. Diese Technik ermöglicht das dynamische Laden von weiteren Inhalten, wenn der Nutzer das Ende der Webseite erreicht. Die Automatisierung dieser Art von Webseiten ist ein lehrreicher Anwendungsfall, da er verschiedene Aspekte der Selenium WebDriver API umfasst. Du kannst zum Beispiel einen ähnlichen Ansatz verwenden, um Webseiten mit Selenium WebDriver zu crawlen. Beispiel 4-3 zeigt einen Test mit einer unendlich scrollenden Seite.

Beispiel 4-3. Test der Ausführung von JavaScript in einer Seite mit unendlichem Scrollen
@Test
void testInfiniteScroll() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/infinite-scroll.html");
    JavascriptExecutor js = (JavascriptExecutor) driver;
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); 1

    By pLocator = By.tagName("p");
    List<WebElement> paragraphs = wait.until(
            ExpectedConditions.numberOfElementsToBeMoreThan(pLocator, 0));
    int initParagraphsNumber = paragraphs.size(); 2

    WebElement lastParagraph = driver.findElement(
            By.xpath(String.format("//p[%d]", initParagraphsNumber))); 3
    String script = "arguments[0].scrollIntoView();";
    js.executeScript(script, lastParagraph); 4

    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(pLocator,
            initParagraphsNumber)); 5
}
1

Wir definieren eine explizite Wartezeit, da wir den Test unterbrechen müssen, bis der neue Inhalt geladen ist.

2

Wir finden die ursprüngliche Anzahl der Absätze auf der Seite.

3

Wir finden den letzten Absatz auf der Seite.

4

Wir scrollen nach unten in dieses Element.

5

Wir warten, bis mehr Absätze auf der Seite verfügbar sind.

Farbwähler

Ein Farbwähler in HTML ist ein Eingabetyp, der es den Nutzern ermöglicht, eine Farbe durch Klicken und Ziehen des Cursors über einen grafischen Bereich auszuwählen. Das Praxis-Webformular enthält eines dieser Elemente (siehe Abbildung 4-2).

hosw 0402
Abbildung 4-2. Farbwähler im Praxis-Webformular

Der folgende Code zeigt das HTML-Markup für den Farbwähler. Beachte, dass ein anfänglicher Farbwert festgelegt wird (ansonsten ist die Standardfarbe schwarz).

<input type="color" class="form-control form-control-color" name="my-colors"
        value="#563d7c">

Beispiel 4-4 zeigt, wie du mit diesem Farbwähler interagieren kannst. Da die Selenium WebDriver API kein Asset zur Steuerung von Farbwählern bereitstellt, verwenden wir JavaScript. Außerdem veranschaulicht dieser Test die Verwendung von Color, einer in der Selenium WebDriver API verfügbaren Unterstützungsklasse für die Arbeit mit Farben.

Beispiel 4-4. Test der Ausführung von JavaScript zur Interaktion mit einem Farbwähler
@Test
void testColorPicker() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html");
    JavascriptExecutor js = (JavascriptExecutor) driver;

    WebElement colorPicker = driver.findElement(By.name("my-colors")); 1
    String initColor = colorPicker.getAttribute("value"); 2
    log.debug("The initial color is {}", initColor);

    Color red = new Color(255, 0, 0, 1); 3
    String script = String.format(
            "arguments[0].setAttribute('value', '%s');", red.asHex());
    js.executeScript(script, colorPicker); 4

    String finalColor = colorPicker.getAttribute("value"); 5
    log.debug("The final color is {}", finalColor);
    assertThat(finalColor).isNotEqualTo(initColor); 6
    assertThat(Color.fromString(finalColor)).isEqualTo(red);
}
1

Wir suchen den Farbwähler nach seinem Namen.

2

Wir lesen den Anfangswert des Farbwählers ab (er sollte #563d7c sein).

3

Wir definieren eine Farbe, mit der wir arbeiten können, indem wir die folgenden RGBA-Komponenten verwenden: rot=255 (maximaler Wert), grün=0 (minimaler Wert), blau=0 (minimaler Wert) und alpha=1 (maximaler Wert, d.h. völlig undurchsichtig).

4

Wir verwenden JavaScript, um den in der Farbauswahl ausgewählten Wert zu ändern. Alternativ können wir die ausgewählte Farbe ändern, indem wir die Anweisung colorPicker.sendKeys(red.asHex()); aufrufen.

5

Wir lesen den Ergebniswert des Farbwählers ab (er sollte #ff0000 sein).

6

Wir stellen fest, dass die Farbe anders ist als der ursprüngliche Wert, aber wie erwartet.

Angepinnte Skripte

Die Selenium WebDriver API ermöglicht es dir, Skripte in Selenium WebDriver 4 anzuheften. Diese Funktion ermöglicht es, JavaScript-Fragmente an eine WebDriver-Sitzung anzuhängen, jedem Snippet einen eindeutigen Schlüssel zuzuweisen und diese Snippets bei Bedarf auszuführen (sogar auf verschiedenen Webseiten). Beispiel 4-5 zeigt einen Test mit angehefteten Skripten.

Beispiel 4-5. Test der Ausführung von JavaScript als angeheftete Skripte
@Test
void testPinnedScripts() {
    String initPage = "https://bonigarcia.dev/selenium-webdriver-java/";
    driver.get(initPage);
    JavascriptExecutor js = (JavascriptExecutor) driver;

    ScriptKey linkKey = js
            .pin("return document.getElementsByTagName('a')[2];"); 1
    ScriptKey firstArgKey = js.pin("return arguments[0];"); 2

    Set<ScriptKey> pinnedScripts = js.getPinnedScripts(); 3
    assertThat(pinnedScripts).hasSize(2); 4

    WebElement formLink = (WebElement) js.executeScript(linkKey); 5
    formLink.click(); 6
    assertThat(driver.getCurrentUrl()).isNotEqualTo(initPage); 7

    String message = "Hello world!";
    String executeScript = (String) js.executeScript(firstArgKey, message); 8
    assertThat(executeScript).isEqualTo(message); 9

    js.unpin(linkKey); 10
    assertThat(js.getPinnedScripts()).hasSize(1); 11
}
1

Wir hängen ein JavaScript-Fragment an, um ein Element auf der Webseite zu finden. Das Gleiche könnten wir auch mit der Standard-WebDriver-API machen. Dennoch verwenden wir diesen Ansatz zu Demozwecken.

2

Wir hängen ein weiteres Stück JavaScript an, das das zurückgibt, was wir ihm als ersten Parameter übergeben.

3

Wir lesen den Satz der angehefteten Skripte.

4

Wir behaupten, dass die Anzahl der angehefteten Skripte wie erwartet ist (d.h. 2).

5

Wir führen das erste angeheftete Skript aus. Als Ergebnis erhalten wir den dritten Link auf der Webseite als WebElement in Java.

6

Wir klicken auf diesen Link, der dem Praxis-Web-Link entsprechen sollte. Daraufhin sollte der Browser zu dieser Seite navigieren.

7

Wir behaupten, dass sich die aktuelle URL von der ursprünglichen unterscheidet.

8

Wir führen das zweite angeheftete Skript aus. Beachte, dass es möglich ist, das angeheftete Skript auszuführen, obwohl sich die Seite im Browser geändert hat (da das Skript an die Sitzung und nicht an eine einzelne Seite gebunden ist).

9

Wir versichern, dass die zurückgegebene Nachricht wie erwartet ist.

10

Wir heften eines der Skripte ab.

11

Wir überprüfen, ob die Anzahl der angehefteten Skripte wie erwartet ist (d.h. 1 zu diesem Zeitpunkt).

Asynchrone Skripte

Die Methode executeAsyncScript() der Schnittstelle JavascriptExecutor ermöglicht die Ausführung von JavaScript-Skripten im Kontext einer Webseite mit Selenium WebDriver. Auf die gleiche Weise wie executeScript() zuvor erklärt, führt executeAsyncScript() eine anonyme Funktion mit dem angegebenen JavaScript-Code im Body der aktuellen Seite aus. Die Ausführung dieser Funktion blockiert den Kontrollfluss von Selenium WebDriver. Der Unterschied besteht darin, dass wir bei executeAsyncScript() die Beendigung des Skripts explizit signalisieren müssen, indem wir einen done-Callback aufrufen ( ). Dieser Callback wird in das ausgeführte Skript als letztes Argument (d.h. arguments[arguments.length - 1]) in der entsprechenden anonymen Funktion eingefügt. Beispiel 4-6 zeigt einen Test, der diesen Mechanismus verwendet.

Beispiel 4-6. Test zur Ausführung von asynchronem JavaScript
@Test
void testAsyncScript() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    JavascriptExecutor js = (JavascriptExecutor) driver;

    Duration pause = Duration.ofSeconds(2); 1
    String script = "const callback = arguments[arguments.length - 1];"
            + "window.setTimeout(callback, " + pause.toMillis() + ");"; 2

    long initMillis = System.currentTimeMillis(); 3
    js.executeAsyncScript(script); 4
    Duration elapsed = Duration
            .ofMillis(System.currentTimeMillis() - initMillis); 5
    log.debug("The script took {} ms to be executed", elapsed.toMillis());
    assertThat(elapsed).isGreaterThanOrEqualTo(pause); 6
}
1

Wir legen eine Pausenzeit von 2 Sekunden fest.

2

Wir definieren das Skript, das ausgeführt werden soll. In der ersten Zeile definieren wir eine Konstante für den Rückruf (d. h. das letzte Skriptargument). Danach verwenden wir die JavaScript-Funktion window.setTimeout(), um die Skriptausführung für eine bestimmte Zeit zu unterbrechen.

3

Wir erhalten die aktuelle Systemzeit (in Millisekunden).

4

Wir führen das Skript aus. Wenn alles wie erwartet funktioniert, wird die Testausführung in dieser Zeile für zwei Sekunden blockiert (wie in Schritt 1 definiert).

5

Wir berechnen die Zeit, die für die Ausführung der vorherigen Zeile benötigt wird.

6

Wir stellen fest, dass die verstrichene Zeit wie erwartet ist (normalerweise einige Millisekunden über der festgelegten Pausenzeit).

Tipp

Ein weiteres Beispiel, das ein asynchrones Skript ausführt, findest du unter "Benachrichtigungen".

Zeitüberschreitungen

Selenium WebDriver ermöglicht die Angabe von drei Arten von Timeouts. Wir können sie nutzen, indem wir die Methode manage().timeouts() in der Selenium WebDriver API aufrufen. Die erste Zeitüberschreitung ist das implizite Warten, das bereits in "Implizites Warten" (als Teil der Wartestrategien) erklärt wurde. Die anderen Optionen sind die Timeouts für das Laden von Seiten und das Laden von Skripten, die im Folgenden erklärt werden.

Zeitüberschreitung beim Laden der Seite

Das Timeout für das Laden einer Seite bietet ein Zeitlimit, um einen Navigationsversuch zu unterbrechen. Mit anderen Worten: Diese Zeitüberschreitung begrenzt die Zeit, in der eine Webseite geladen wird. Wenn diese Zeitüberschreitung (der Standardwert ist 30 Sekunden) überschritten wird, wird eine Ausnahme ausgelöst. Beispiel 4-7 zeigt ein Beispiel für diese Zeitüberschreitung. Wie du siehst, ist dieser Code eine Dummy-Implementierung eines negativen Tests. Mit anderen Worten: Er prüft unerwartete Bedingungen im SUT.

Beispiel 4-7. Test mit einer Zeitüberschreitung beim Laden der Seite
@Test
void testPageLoadTimeout() {
    driver.manage().timeouts().pageLoadTimeout(Duration.ofMillis(1)); 1

    assertThatThrownBy(() -> driver
            .get("https://bonigarcia.dev/selenium-webdriver-java/"))
                    .isInstanceOf(TimeoutException.class); 2
}
1

Wir geben die kleinstmögliche Zeitspanne zum Laden der Seite an, nämlich eine Millisekunde.

2

Wir laden eine Webseite. Dieser Aufruf (implementiert als Java-Lambda) wird fehlschlagen, da es unmöglich ist, diese Webseite in weniger als einer Millisekunde zu laden. Aus diesem Grund wird erwartet, dass die Ausnahme TimeoutException im Lambda ausgelöst wird, indem die Methode AssertJ assertThatThrownBy verwendet wird.

Hinweis

Du kannst mit diesem Test spielen, indem du die Timeout-Deklaration entfernst (d.h. Schritt 1). Wenn du das tust, schlägt der Test fehl, da eine Ausnahme erwartet, aber nicht ausgelöst wird.

Zeitüberschreitung beim Laden des Skripts

Das Zeitlimit für das Laden von Skripten bietet ein Zeitlimit für die Unterbrechung eines Skripts, das gerade ausgewertet wird. Der Standardwert für diese Zeitüberschreitung beträgt dreihundert Sekunden. Beispiel 4-8 zeigt einen Test mit einem Zeitlimit für das Laden von Skripten.

Beispiel 4-8. Test mit einer Zeitüberschreitung beim Laden eines Skripts
@Test
void testScriptTimeout() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    JavascriptExecutor js = (JavascriptExecutor) driver;
    driver.manage().timeouts().scriptTimeout(Duration.ofSeconds(3)); 1

    assertThatThrownBy(() -> {
        long waitMillis = Duration.ofSeconds(5).toMillis();
        String script = "const callback = arguments[arguments.length - 1];"
                + "window.setTimeout(callback, " + waitMillis + ");"; 2
        js.executeAsyncScript(script);
    }).isInstanceOf(ScriptTimeoutException.class); 3
}
1

Wir definieren eine Skript-Zeitüberschreitung von drei Sekunden. Das bedeutet, dass ein Skript, das länger als diese Zeit dauert, eine Ausnahme auslöst.

2

Wir führen ein asynchrones Skript aus, das die Ausführung fünf Sekunden lang pausiert.

3

Die Ausführungszeit des Skripts ist größer als der konfigurierte Skript-Timeout, was zu einer ScriptTimeoutException führt. Auch dieses Beispiel ist ein negativer Test, d.h. es wurde so konzipiert, dass diese Ausnahme erwartet wird.

Screenshots

Selenium WebDriver wird hauptsächlich verwendet, um funktionale End-to-End-Tests von Webanwendungen durchzuführen. Mit anderen Worten: Wir verwenden ihn, um zu überprüfen, ob sich Webanwendungen wie erwartet verhalten, wenn wir mit ihrer Benutzeroberfläche (d. h. mit einem Webbrowser) interagieren. Dieser Ansatz ist sehr praktisch, um High-Level-Benutzerszenarien zu automatisieren, aber er birgt auch verschiedene Schwierigkeiten. Eine der größten Herausforderungen bei End-to-End-Tests besteht darin, die Ursache für einen fehlgeschlagenen Test zu ermitteln. Angenommen, der Fehler ist legitim (d. h. er wurde nicht durch einen schlecht implementierten Test verursacht), dann kann die Ursache ganz unterschiedlich sein: auf der Client-Seite (z. B. fehlerhafte JavaScript-Logik), auf der Server-Seite (z. B. eine interne Ausnahme) oder bei der Integration mit anderen Komponenten (z. B. unzureichender Zugriff auf die Datenbank), neben anderen Gründen. Einer der häufigsten Mechanismen, die in Selenium WebDriver zur Fehleranalyse eingesetzt werden, ist das Erstellen von Browser-Screenshots. In diesem Abschnitt werden die von der Selenium WebDriver API bereitgestellten Mechanismen vorgestellt.

Tipp

In"Fehleranalyse" werden die Framework-spezifischen Techniken erläutert, mit denen festgestellt werden kann, wann ein Test fehlgeschlagen ist, um verschiedene Fehleranalysetechniken wie Screenshots, Aufzeichnungen und Logsammlungen durchzuführen.

Selenium WebDriver bietet die Schnittstelle TakesScreenshot für die Erstellung von Browser-Screenshots. Jedes Treiberobjekt, das von RemoteWebDriver erbt (siehe Abbildung 2-2), implementiert ebenfalls diese Schnittstelle. Wir können also ein WebDriver Objekt, das einen der wichtigsten Browser instanziiert (z.B. ChromeDriver, FirefoxDriver, etc.), wie folgt casten:

WebDriver driver = new ChromeDriver();
TakesScreenshot ts = (TakesScreenshot) driver;

Die Schnittstelle TakesScreenshot bietet nur eine Methode namens getScreenshotAs(OutputType<X> target), um Screenshots zu erstellen. Der Parameter OutputType<X> target bestimmt den Screenshot-Typ und den zurückgegebenen Wert. Tabelle 4-2 zeigt die verfügbaren Alternativen für diesen Parameter.

Tabelle 4-2. OutputType-Parameter
Parameter Beschreibung Rückgabe Beispiel
OutputType.FILE

Mache einen Screenshot als PNG-Datei (in einem temporären Systemverzeichnis)

File
File screenshot =
    ts.getScreenshotAs(
    OutputType.FILE);
OutputType.BASE64

Erstelle einen Screenshot im Base64-Format (d.h. als ASCII-String kodiert)

String
String screenshot =
    ts.getScreenshotAs(
    OutputType.BASE64);
OutputType.BYTES

Einen Screenshot als rohes Byte-Array erstellen

byte[]
byte[] screenshot =
    ts.getScreenshotAs(
    OutputType.BYTES);
Tipp

Mit der Methode getScreenshotAs() können Screenshots des Browser-Viewports erstellt werden. Darüber hinaus ermöglicht Selenium WebDriver 4 die Erstellung von Ganzseiten-Screenshots mit verschiedenen Mechanismen (siehe "Ganzseiten-Screenshot").

Beispiel 4-9 zeigt einen Test zur Erstellung eines Browser-Screenshots im PNG-Format. Beispiel 4-10 zeigt einen weiteren Test, um einen Screenshot als Base64-String zu erstellen. Der resultierende Screenshot ist in Abbildung 4-3 zu sehen.

Beispiel 4-9. Einen Screenshot als PNG-Datei erstellen
@Test
void testScreenshotPng() throws IOException {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    TakesScreenshot ts = (TakesScreenshot) driver;

    File screenshot = ts.getScreenshotAs(OutputType.FILE); 1
    log.debug("Screenshot created on {}", screenshot);

    Path destination = Paths.get("screenshot.png"); 2
    Files.move(screenshot.toPath(), destination, REPLACE_EXISTING); 3
    log.debug("Screenshot moved to {}", destination);

    assertThat(destination).exists(); 4
}
1

Wir machen den Browser-Bildschirm zu einer PNG-Datei.

2

Diese Datei befindet sich standardmäßig in einem temporären Ordner, also verschieben wir sie in eine neue Datei namens screenshot.png (im Stammordner des Projekts).

3

Wir verwenden Standard-Java, um die Screenshot-Datei an den neuen Ort zu verschieben.

4

Wir verwenden Assertions, um zu überprüfen, ob die Zieldatei existiert.

hosw 0403
Abbildung 4-3. Browser-Screenshot der Indexseite der Praxisseite
Beispiel 4-10. Test, um einen Screenshot als Base64 zu erstellen
@Test
void testScreenshotBase64() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    TakesScreenshot ts = (TakesScreenshot) driver;

    String screenshot = ts.getScreenshotAs(OutputType.BASE64); 1
    log.debug("Screenshot in base64 "
          + "(you can copy and paste it into a browser navigation bar to watch it)\n"
          + "data:image/png;base64,{}", screenshot); 2
    assertThat(screenshot).isNotEmpty(); 3
}
1

Wir machen den Browser-Bildschirm im Base64-Format.

2

Wir hängen das Präfix data:image/png;base64, an den Base64-String an und protokollieren ihn in der Standardausgabe. Du kannst die resultierende Zeichenkette kopieren und in die Navigationsleiste eines Browsers einfügen, um das Bild anzuzeigen.

3

Wir behaupten, dass der Screenshot-String einen Inhalt hat.

Hinweis

Die Protokollierung des Screenshots in Base64 wie im vorherigen Beispiel könnte sehr nützlich sein, um Fehler zu diagnostizieren, wenn wir Tests auf CI-Servern durchführen, bei denen wir keinen Zugriff auf das System haben (z. B. GitHub Actions).

WebElement Screenshots

Die Schnittstelle WebElement erweitert die Schnittstelle TakesScreenshot. Auf diese Weise ist es möglich, Teil-Screenshots des sichtbaren Inhalts eines bestimmten Webelements zu erstellen. (Siehe Beispiel 4-11.) Beachte, dass dieser Test dem vorherigen mit PNG-Dateien sehr ähnlich ist, aber in diesem Fall rufen wir die Methode getScreenshotAs() direkt mit einem Webelement auf. Abbildung 4-4 zeigt den resultierenden Screenshot.

Beispiel 4-11. Testen der Erstellung eines Teilbildschirms als PNG-Datei
@Test
void testWebElementScreenshot() throws IOException {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html");

    WebElement form = driver.findElement(By.tagName("form"));
    File screenshot = form.getScreenshotAs(OutputType.FILE);
    Path destination = Paths.get("webelement-screenshot.png");
    Files.move(screenshot.toPath(), destination, REPLACE_EXISTING);

    assertThat(destination).exists();
}
hosw 0404
Abbildung 4-4. Teilweiser Screenshot des Praxis-Webformulars

Größe und Position des Fensters

Die Selenium WebDriver API ermöglicht es, die Größe und Position des Browsers sehr einfach über die Window Schnittstelle zu manipulieren. Auf diesen Typ kann von einem Treiberobjekt aus mit der folgenden Anweisung zugegriffen werden. Tabelle 4-3 zeigt die verfügbaren Methoden in dieser Schnittstelle. Beispiel 4-12 zeigt einen grundlegenden Test über diese Funktion.

Window window = driver.manage().window();
Tabelle 4-3. Fenster-Methoden
Methode Rückgabe Beschreibung
getSize()
Dimension

Ermittelt die aktuelle Fenstergröße. Sie gibt die äußere Abmessung des Fensters zurück, nicht nur den Viewport (d. h. den sichtbaren Bereich einer Webseite für Endnutzer).

setSize(Dimension
    targetSize)
void

Ändere die aktuelle Fenstergröße (auch hier wieder die äußere Abmessung und nicht das Ansichtsfenster).

getPosition()
Point

Ermittelt die aktuelle Fensterposition (relativ zur oberen linken Ecke des Bildschirms).

setPosition(Point
    targetPosition)
void

Ändere die aktuelle Fensterposition (wieder relativ zur oberen linken Ecke des Bildschirms).

maximize()
void

Maximiere das aktuelle Fenster.

minimize()
void

Minimiere das aktuelle Fenster.

fullscreen()
void

Vergrößert den Bildschirm des aktuellen Fensters.

Beispiel 4-12. Test zum Lesen und Ändern der Browsergröße und -position
@Test
void testWindow() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    Window window = driver.manage().window();

    Point initialPosition = window.getPosition(); 1
    Dimension initialSize = window.getSize(); 2
    log.debug("Initial window: position {} -- size {}", initialPosition,
            initialSize);

    window.maximize(); 3

    Point maximizedPosition = window.getPosition();
    Dimension maximizedSize = window.getSize();
    log.debug("Maximized window: position {} -- size {}", maximizedPosition,
            maximizedSize);

    assertThat(initialPosition).isNotEqualTo(maximizedPosition); 4
    assertThat(initialSize).isNotEqualTo(maximizedSize);
}
1

Wir lesen die Position des Fensters ab.

2

Wir lesen die Fenstergröße ab.

3

Wir maximieren das Browserfenster.

4

Wir überprüfen, dass sich die maximierte Position (und Größe, in der folgenden Zeile) vom ursprünglichen Fenster unterscheidet.

Browser-Verlauf

Selenium WebDriver ermöglicht die Manipulation des Browserverlaufs über die Schnittstelle Navigation. Die folgende Anweisung veranschaulicht, wie man von einem WebDriver Objekt auf diese Schnittstelle zugreift. Die Verwendung dieser Schnittstelle ist recht einfach. Tabelle 4-4 zeigt ihre öffentlichen Methoden, und Beispiel 4-13 zeigt ein grundlegendes Beispiel. Beachte, dass dieser Test mit diesen Methoden zu verschiedenen Webseiten navigiert und am Ende des Tests überprüft, ob die URL der Webseite den Erwartungen entspricht.

Navigation navigation = driver.navigate();

Der Schatten DOM

Wie in "The Document Object Model (DOM)" vorgestellt , ist das DOM eine Programmierschnittstelle, die es uns ermöglicht, eine Webseite mithilfe einer Baumstruktur darzustellen und zu manipulieren. Das Shadow-DOM ist eine Funktion dieser Programmierschnittstelle, die die Erstellung von Teilbäumen innerhalb des regulären DOM-Baums ermöglicht. Das Schatten-DOM ermöglicht die Kapselung einer Gruppe von DOM-Teilbäumen ( Schattenbaum genannt, wie in Abbildung 4-5 dargestellt), die andere CSS-Stile als das ursprüngliche DOM festlegen können. Der Knoten im regulären DOM, an den der Schattenbaum angehängt ist, wird als Shadow Host bezeichnet. Der Wurzelknoten des Schattenbaums wird Schattenwurzel genannt. Wie in Abbildung 4-5 dargestellt, wird der Schattenbaum in einem einzigen zusammengesetzten Baum in das ursprüngliche DOM eingefügt, um im Browser gerendert zu werden.

hosw 0405
Abbildung 4-5. Schematische Darstellung des Schatten-DOMs
Hinweis

Das Shadow DOM ist Teil des Standardpakets (zusammen mit HTML-Vorlagen oder benutzerdefinierten Elementen), das die Implementierung von Webkomponenten (d. h. wiederverwendbaren benutzerdefinierten Elementen für Webanwendungen) ermöglicht.

Das Schatten-DOM ermöglicht die Erstellung von in sich geschlossenen Komponenten. Mit anderen Worten: Der Schattenbaum ist vom ursprünglichen DOM isoliert. Diese Funktion ist nützlich für Webdesign und Komposition, kann aber für automatisierte Tests mit Selenium WebDriver eine Herausforderung darstellen (da die regulären Lokalisierungsstrategien keine Webelemente innerhalb des Schattenbaums finden können). Glücklicherweise bietet Selenium WebDriver 4 eine WebElement Methode, die den Zugriff auf das Shadow DOM ermöglicht. Beispiel 4-14 demonstriert diese Anwendung.

Beispiel 4-14. Test zum Lesen des Schatten-DOMs
@Test
void testShadowDom() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/shadow-dom.html"); 1

    WebElement content = driver.findElement(By.id("content")); 2
    SearchContext shadowRoot = content.getShadowRoot(); 3
    WebElement textElement = shadowRoot.findElement(By.cssSelector("p")); 4
    assertThat(textElement.getText()).contains("Hello Shadow DOM"); 5
}
1

Wir öffnen die Übungswebseite, die einen Schattenbaum enthält. Du kannst den Quellcode dieser Seite einsehen, um die JavaScript-Methode zu überprüfen, mit der ein Schattenbaum erstellt wird.

2

Wir lokalisieren das Schattenwirt-Element.

3

Wir erhalten die Schattenwurzel vom Host-Element. Als Ergebnis erhalten wir eine Instanz von SearchContext, einer Schnittstelle, die von WebDriver und WebElement implementiert wird, und die es uns ermöglicht, Elemente mit den Methoden findElement() und find​Ele⁠ments() zu finden.

4

Wir finden das erste Absatzelement im Schattenbaum.

5

Wir überprüfen, ob der Textinhalt des Schattenelements wie erwartet ist.

Warnung

Diese Funktion der W3C WebDriver-Spezifikation ist zum Zeitpunkt der Erstellung dieses Dokuments neu und daher möglicherweise nicht in allen Treibern (z. B. chromedriver, geckodriver) implementiert. Zum Beispiel ist sie ab Version 96 von Chrome und Edge verfügbar.

Cookies

HTTP 1.x ist ein zustandsloses Protokoll, was bedeutet, dass der Server den Status des Benutzers nicht verfolgt. Mit anderen Worten: Webserver erinnern sich nicht an die Benutzer bei verschiedenen Anfragen. Der Cookie-Mechanismus ist eine Erweiterung von HTTP, die es ermöglicht, Benutzer zu verfolgen, indem sie kleine Textstücke, Cookies genannt, vom Server zum Client sendet. Diese Cookies müssen von den Kunden zurückgeschickt werden, so dass sich die Server an ihre Kunden erinnern. Cookies ermöglichen es unter anderem, Websitzungen aufrechtzuerhalten oder das Nutzererlebnis auf der Website zu personalisieren.

Webbrowser ermöglichen die manuelle Verwaltung der Browser-Cookies. Selenium WebDriver ermöglicht eine äquivalente Manipulation, allerdings programmatisch. Die Selenium WebDriver API bietet dafür die in Tabelle 4-5 aufgeführten Methoden. Sie sind über die Funktion manage() eines WebDriver Objekts zugänglich.

Tabelle 4-5. Methoden zur Verwaltung von Cookies
Methode Rückgabe Beschreibung
addCookie(Cookie cookie)
void

Ein neues Cookie hinzufügen

deleteCookieNamed(String name)
void

Ein bestehendes Cookie nach Namen löschen

deleteCookie(Cookie cookie)
void

Ein bestehendes Cookie per Instanz löschen

deleteAllCookies()
void

Alle Cookies löschen

getCookies()
Set<Cookie>

Alle Cookies erhalten

getCookieNamed(String name)
Cookie

Ein Cookie nach Name abrufen

Wie diese Tabelle zeigt, bietet die Klasse Cookie eine Abstraktion für ein einzelnes Cookie in Java. Tabelle 4-6 fasst die in dieser Klasse verfügbaren Methoden zusammen. Außerdem hat diese Klasse mehrere Konstruktoren, die die folgenden Parameter akzeptieren:

String name

Cookie-Name (obligatorisch)

String value

Cookie-Wert (obligatorisch)

String domain

Domain, in der das Cookie sichtbar ist (optional)

String path

Pfad, in dem das Cookie sichtbar ist (optional)

Date expiry

Ablaufdatum des Cookies (optional)

boolean isSecure

Ob das Cookie eine sichere Verbindung erfordert (optional)

boolean isHttpOnly

Ob es sich bei diesem Cookie um ein HTTP-Only-Cookie handelt, d. h., das Cookie ist nicht über ein clientseitiges Skript zugänglich (optional)

String sameSite

Ob es sich bei diesem Cookie um ein Same-Site-Cookie handelt, d. h., das Cookie ist auf einen First-Party- oder Same-Site-Kontext beschränkt (optional)

Die folgenden Beispiele zeigen verschiedene Tests, die Web-Cookies mit der Selenium WebDriver API verwalten. Diese Beispiele verwenden eine Übungswebseite, die die Website-Cookies auf der GUI anzeigt (siehe Abbildung 4-6):

hosw 0406
Abbildung 4-6. Übungswebseite für Web-Cookies
Beispiel 4-15. Test zum Lesen vorhandener Cookies
@Test
void testReadCookies() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/cookies.html");

    Options options = driver.manage(); 1
    Set<Cookie> cookies = options.getCookies(); 2
    assertThat(cookies).hasSize(2);

    Cookie username = options.getCookieNamed("username"); 3
    assertThat(username.getValue()).isEqualTo("John Doe"); 4
    assertThat(username.getPath()).isEqualTo("/");

    driver.findElement(By.id("refresh-cookies")).click(); 5
}
1

Wir erhalten das Options Objekt, das zur Verwaltung von Cookies verwendet wird.

2

Wir lesen alle verfügbaren Cookies auf dieser Seite. Sie sollte zwei Cookies enthalten.

3

Wir lesen den Cookie mit dem Namen username.

4

Der Wert des vorherigen Cookies sollte John Doe sein.

5

Die letzte Anweisung hat keine Auswirkungen auf den Test. Wir rufen diesen Befehl auf, um die Cookies in der Browser-GUI zu prüfen.

Beispiel 4-16. Test zum Hinzufügen neuer Cookies
@Test
void testAddCookies() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/cookies.html");

    Options options = driver.manage();
    Cookie newCookie = new Cookie("new-cookie-key", "new-cookie-value"); 1
    options.addCookie(newCookie); 2
    String readValue = options.getCookieNamed(newCookie.getName())
            .getValue(); 3
    assertThat(newCookie.getValue()).isEqualTo(readValue); 4

    driver.findElement(By.id("refresh-cookies")).click();
}
1

Wir erstellen ein neues Cookie.

2

Wir fügen den Cookie zur aktuellen Seite hinzu.

3

Wir lesen den Wert des gerade hinzugefügten Cookies.

4

Wir überprüfen, ob dieser Wert den Erwartungen entspricht.

Beispiel 4-17. Test der Bearbeitung bestehender Cookies
@Test
void testEditCookie() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/cookies.html");

    Options options = driver.manage();
    Cookie username = options.getCookieNamed("username"); 1
    Cookie editedCookie = new Cookie(username.getName(), "new-value"); 2
    options.addCookie(editedCookie); 3

    Cookie readCookie = options.getCookieNamed(username.getName()); 4
    assertThat(editedCookie).isEqualTo(readCookie); 5

    driver.findElement(By.id("refresh-cookies")).click();
}
1

Wir lesen ein bestehendes Cookie.

2

Wir erstellen ein neues Cookie, das den vorherigen Cookie-Namen verwendet.

3

Wir fügen das neue Cookie zur Webseite hinzu.

4

Wir lesen den gerade hinzugefügten Keks.

5

Wir überprüfen, ob das Cookie korrekt bearbeitet wurde.

Beispiel 4-18. Test zum Löschen bestehender Cookies
@Test
void testDeleteCookies() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/cookies.html");

    Options options = driver.manage();
    Set<Cookie> cookies = options.getCookies(); 1
    Cookie username = options.getCookieNamed("username"); 2
    options.deleteCookie(username); 3

    assertThat(options.getCookies()).hasSize(cookies.size() - 1); 4

    driver.findElement(By.id("refresh-cookies")).click();
}
1

Wir lesen alle Cookies.

2

Wir lesen den Cookie mit dem Namen username.

3

Wir löschen das vorherige Cookie.

4

Wir überprüfen, ob die Größe der Cookies den Erwartungen von entspricht.

Dropdown-Listen

Ein typisches Element in Webformularen sind Dropdown-Listen. Diese Felder ermöglichen es den Nutzern, ein oder mehrere Elemente innerhalb einer Optionsliste auszuwählen. Die klassischen HTML-Tags, die zur Darstellung dieser Felder verwendet werden, sind <select> und <options>. Wie üblich enthält das Praxis-Webformular eines dieser Elemente (siehe Abbildung 4-7), das in HTML wie folgt definiert ist:

<select class="form-select" name="my-select">
  <option selected>Open this select menu</option>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
</select>
hosw 0407
Abbildung 4-7. Feld im Praxis-Webformular auswählen

Diese Elemente sind in Webformularen sehr weit verbreitet. Aus diesem Grund bietet Selenium WebDriver eine Hilfsklasse namens Select, um ihre Handhabung zu vereinfachen. Diese Klasse umhüllt eine Auswahl WebElement und bietet eine Vielzahl von Funktionen. Tabelle 4-7 fasst die öffentlichen Methoden von zusammen, die in der Klasse Select verfügbar sind. Beispiel 4-19 zeigt einen einfachen Test mit dieser Klasse.

Tabelle 4-7. Methoden auswählen
Methode Rückgabe Beschreibung
Select(WebElement element)
Select

Konstruktor, der ein WebElement als Parameter verwendet (es muss ein <select> Element sein); andernfalls wirft er ein UnexpectedTagNameException

getWrappedElement()
WebElement

Get wrapped WebElement (d.h. die im Konstruktor verwendete Version)

isMultiple()
boolean

Ob das Select-Element die Auswahl mehrerer Optionen unterstützt

getOptions()
List<WebElement>

Alle Optionen lesen, die zum Select-Element gehören

getAllSelectedOptions()
List<WebElement>

Alle ausgewählten Optionen lesen

getFirstSelectedOption()
WebElement

Zuerst lesen ausgewählte Option

selectByVisibleText(String text)
void

Wähle alle Optionen aus, die einem bestimmten angezeigten Text entsprechen

selectByIndex(int index)
void

Wähle eine Option nach Indexnummer

selectByValue(String value)
void

Option(en) nach Wertattribut auswählen

deselectAll()
void

Alle Optionen abwählen

deselectByValue(String value)
void

Option(en) nach Wertattribut abwählen

deselectByIndex(int index)
void

Abwählen nach Indexnummer

deselectByVisibleText(String text)
void

Deselektiere Optionen, die mit einem bestimmten angezeigten Text übereinstimmen

Beispiel 4-19. Test der Interaktion mit einem Auswahlfeld
@Test
void test() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html");

    Select select = new Select(driver.findElement(By.name("my-select"))); 1
    String optionLabel = "Three";
    select.selectByVisibleText(optionLabel); 2

    assertThat(select.getFirstSelectedOption().getText())
            .isEqualTo(optionLabel); 3
}
1

Wir suchen das Select-Element anhand seines Namens und verwenden die resultierende WebElement, um ein Select Objekt zu instanziieren.

2

Wir wählen eine der Optionen aus, die in dieser Auswahl zur Verfügung stehen, indem wir eine By-Text-Strategie anwenden.

3

Wir überprüfen, ob der ausgewählte Optionstext den Erwartungen entspricht.

Elemente der Datenliste

Eine andere Möglichkeit, Dropdown-Listen in HTML zu implementieren, ist die Verwendung von Datenlisten. Obwohl Datenlisten aus grafischer Sicht den Auswahlelementen sehr ähnlich sind, gibt es einen klaren Unterschied zwischen ihnen. Einerseits zeigen Auswahlfelder eine Optionsliste an, und die Benutzer/innen wählen eine (oder mehrere) der verfügbaren Optionen aus. Andererseits zeigen Datenlisten eine Liste mit vorgeschlagenen Optionen an, die mit einem Eingabefeld (Textfeld) verbunden sind, und die Nutzer können einen dieser vorgeschlagenen Werte auswählen oder einen eigenen Wert eingeben. Das Praxis-Webformular enthält eine dieser Datenlisten. Das Markup findest du im folgenden Snippet und einen Screenshot in Abbildung 4-8.

<input class="form-control" list="my-options" name="my-datalist"
        placeholder="Type to search...">
<datalist id="my-options">
  <option value="San Francisco">
  <option value="New York">
  <option value="Seattle">
  <option value="Los Angeles">
  <option value="Chicago">
</datalist>
hosw 0408
Abbildung 4-8. Datenlistenfeld im Webformular der Praxis

Selenium WebDriver bietet keine benutzerdefinierte Hilfsklasse für die Bearbeitung von Datenlisten. Stattdessen müssen wir mit ihnen wie mit Standard-Eingabetexten interagieren, mit dem Unterschied, dass ihre Optionen beim Klicken auf das Eingabefeld angezeigt werden. Beispiel 4-20 zeigt einen Test, der dies veranschaulicht.

Beispiel 4-20. Test der Interaktion mit einem Datenlistenfeld
@Test
void testDatalist() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html");

    WebElement datalist = driver.findElement(By.name("my-datalist")); 1
    datalist.click(); 2

    WebElement option = driver
            .findElement(By.xpath("//datalist/option[2]")); 3
    String optionValue = option.getAttribute("value"); 4
    datalist.sendKeys(optionValue); 5

    assertThat(optionValue).isEqualTo("New York"); 6
}
1

Wir suchen das Eingabefeld, das für die Datenliste verwendet wird.

2

Wir klicken darauf, um die Optionen anzuzeigen.

3

Wir finden die zweite Option.

4

Wir lesen den Wert der gefundenen Option ab.

5

Wir geben den Wert in das Eingabefeld ein.

6

Wir behaupten, dass der Optionswert wie erwartet ist.

Navigation Ziele

Wenn wir mit einem Browser durch Webseiten navigieren, verwenden wir standardmäßig eine einzelne Seite, die der URL in der Navigationsleiste entspricht. Dann können wir eine weitere Seite in einem neuen Browser-Tab öffnen. Dieser zweite Tab kann explizit geöffnet werden, wenn ein Link das Attribut target definiert, oder der Nutzer kann die Navigation zu einem neuen Tab erzwingen, indem er die Modifikatortaste Strg (oder Cmd in macOS) zusammen mit dem Mausklick in einen Weblink verwendet. Eine weitere Möglichkeit ist , Webseiten in neuen Fenstern zu öffnen. Dazu verwenden Webseiten normalerweise den JavaScript-Befehl window.open(url). Eine andere Möglichkeit, verschiedene Seiten gleichzeitig anzuzeigen, ist die Verwendung von Frames und iframes. Ein Frame ist ein HTML-Elementtyp, der einen bestimmten Bereich (in einem sogenannten Frameset) definiert, in dem eine Webseite angezeigt werden kann. Ein iframe ist ein weiteres HTML-Element, das die Einbettung einer HTML-Seite in die aktuelle Seite ermöglicht.

Warnung

Die Verwendung von Frames wird nicht empfohlen, da diese Elemente viele Nachteile haben, wie z. B. Probleme mit der Leistung und der Barrierefreiheit. Ich erkläre, wie du sie aus Kompatibilitätsgründen mit Selenium WebDriver verwenden kannst. Dennoch empfehle ich dringend, Frames bei brandneuen Webanwendungen zu vermeiden.

Die Selenium WebDriver API bietet die Schnittstelle TargetLocator zum Umgang mit den zuvor genannten Zielen (d.h. Tabs, Fenster, Frames und Iframes). Diese Schnittstelle ermöglicht es, den Fokus der zukünftigen Befehle eines WebDriver Objekts zu ändern (auf einen neuen Tab, ein neues Fenster usw.). Auf diese Schnittstelle kann man zugreifen, indem man die Methode switchTo() in einem WebDriver Objekt aufruft.Tabelle 4-8 beschreibt die öffentlichen Methoden.

Tabelle 4-8. TargetLocator Methoden
Methode Rückgabe Beschreibung
frame(int index)
WebDriver

Wechsle den Fokus auf einen Frame (oder Iframe) nach Indexnummer.

frame(String
    nameOrId)
WebDriver

Ändere den Fokus auf einen Frame (oder iframe) nach Name oder id.

frame(WebElement
    frameElement)
WebDriver

Ändere den Fokus auf einen Frame (oder iframe), der sich zuvor als WebElement befand.

parentFrame()
WebDriver

Wechsle den Fokus auf den übergeordneten Kontext.

window(String
    nameOrHandle)
WebDriver

Wechsle den Fokus auf zu einem anderen Fenster, nach Name oder Handle. Ein Fensterhandle ist eine hexadezimale Zeichenfolge, die ein Fenster oder eine Registerkarte eindeutig identifiziert.

newWindow(WindowType
    typeHint)
WebDriver

Erstellt ein neues Browserfenster (mit WindowType.WINDOW) oder einen Tab (WindowType.TAB) und setzt den Fokus darauf.

defaultContent()
WebDriver

Wähle das Hauptdokument (bei Verwendung von Iframes) oder den ersten Frame auf der Seite (bei Verwendung eines Framesets).

activeElement()
WebElement

Ermittelt das aktuell ausgewählte Element.

alert()
Alert

Ändere den Fokus auf eine Fenstermeldung (siehe "Dialogfelder" für weitere Informationen).

Registerkarten und Fenster

Beispiel 4-21 zeigt einen Test, bei dem wir einen neuen Tab für die Navigation auf einer zweiten Webseite öffnen. Beispiel 4-22 zeigt einen ähnlichen Fall, bei dem ein neues Fenster für die zweite Webseite geöffnet wird. Beachte, dass der Unterschied zwischen diesen Beispielen nur in den Parametern WindowType.TAB und WindowType.WINDOW besteht.

Beispiel 4-21. Test zum Öffnen einer neuen Registerkarte
@Test
void testNewTab() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/"); 1
    String initHandle = driver.getWindowHandle(); 2

    driver.switchTo().newWindow(WindowType.TAB); 3
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html"); 4
    assertThat(driver.getWindowHandles().size()).isEqualTo(2); 5

    driver.switchTo().window(initHandle); 6
    driver.close(); 7
    assertThat(driver.getWindowHandles().size()).isEqualTo(1); 8
}
1

Wir navigieren zu einer Webseite.

2

Wir erhalten das aktuelle Fensterhandle.

3

Wir öffnen eine neue Registerkarte und ändern den Fokus auf diese.

4

Wir öffnen eine weitere Webseite (da der Fokus im zweiten Tab liegt, wird die Seite im zweiten Tab geöffnet).

5

Wir überprüfen, dass die Anzahl der Fenstergriffe an dieser Stelle 2 beträgt.

6

Wir ändern den Fokus von auf das ursprüngliche Fenster (mithilfe seines Handles).

7

Wir schließen nur das aktuelle Fenster. Die zweite Registerkarte bleibt geöffnet.

8

Wir überprüfen, dass die Anzahl der Fenstergriffe jetzt 1 ist.

Beispiel 4-22. Test zum Öffnen eines neuen Fensters
@Test
void testNewWindow() {
    driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
    String initHandle = driver.getWindowHandle();

    driver.switchTo().newWindow(WindowType.WINDOW); 1
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-form.html");
    assertThat(driver.getWindowHandles().size()).isEqualTo(2);

    driver.switchTo().window(initHandle);
    driver.close();
    assertThat(driver.getWindowHandles().size()).isEqualTo(1);
}
1

Diese Zeile ist in den Beispielen anders. In diesem Fall öffnen wir ein neues Fenster (statt eines Tabs) und fokussieren darauf.

Rahmen und Iframes

Beispiel 4-23 zeigt einen Test, bei dem die zu testende Webseite einen iframe enthält. Beispiel 4-24 zeigt den gleichen Fall auf , allerdings mit einem Frameset.

Beispiel 4-23. Test zum Umgang mit iframes
@Test
void testIFrames() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/iframes.html"); 1

    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    wait.until(ExpectedConditions
            .frameToBeAvailableAndSwitchToIt("my-iframe")); 2

    By pName = By.tagName("p");
    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(pName, 0)); 3
    List<WebElement> paragraphs = driver.findElements(pName);
    assertThat(paragraphs).hasSize(20); 4
}
1

Wir öffnen eine Webseite, die einen Iframe enthält (siehe Abbildung 4-9).

2

Wir verwenden ein explizites Warten, um auf den Frame zu warten und zu ihm zu wechseln.

3

Wir verwenden eine weitere explizite Wartezeit, um zu warten, bis die im iframe enthaltenen Absätze verfügbar sind.

4

Wir behaupten, dass die Anzahl der Absätze wie erwartet ist.

hosw 0409
Abbildung 4-9. Übungswebseite mit einem iframe
Beispiel 4-24. Test Umgang mit Frames
@Test
void testFrames() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/frames.html"); 1

    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
    String frameName = "frame-body";
    wait.until(ExpectedConditions
            .presenceOfElementLocated(By.name(frameName))); 2
    driver.switchTo().frame(frameName); 3

    By pName = By.tagName("p");
    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(pName, 0));
    List<WebElement> paragraphs = driver.findElements(pName);
    assertThat(paragraphs).hasSize(20);
}
1

Wir öffnen eine Webseite , die ein Frameset enthält (siehe Abbildung 4-10).

2

Wir warten, bis der Rahmen verfügbar ist. Beachte, dass die Schritte 2 und 3 in Beispiel 4-23 mit diesem Schritt gleichzusetzen sind.

3

Wir ändern den Fokus auf diesen Rahmen.

hosw 0410
Abbildung 4-10. Übungswebseite mit Frames

Dialogboxen

JavaScript bietet verschiedene Dialogfelder (manchmal auch Pop-ups genannt), um mit dem Benutzer zu interagieren, nämlich:

Alert

Um eine Nachricht anzuzeigen und darauf zu warten, dass der Benutzer die Schaltfläche OK drückt (einzige Auswahlmöglichkeit im Dialog). Der folgende Code öffnet zum Beispiel einen Dialog, der "Hallo Welt!" anzeigt und darauf wartet, dass der Benutzer die Schaltfläche OK drückt.

alert("Hello world!");
Bestätige

Um ein Dialogfeld mit einer Frage und zwei Schaltflächen anzuzeigen: OK und Abbrechen. Der folgende Code öffnet z. B. ein Dialogfeld mit der Meldung "Ist das richtig?" und der Eingabeaufforderung, auf OK oder Abbrechen zu klicken.

let correct = confirm("Is this correct?");
Eingabeaufforderung

So zeigst du ein Dialogfeld mit einer Textnachricht, einem Eingabetextfeld und den Schaltflächen OK und Abbrechen an. Der folgende Code zeigt zum Beispiel ein Pop-up mit der Meldung "Bitte geben Sie Ihren Namen ein", ein Dialogfeld, in dem der Benutzer etwas eingeben kann, und zwei Schaltflächen (OK und Abbrechen).

let username = prompt("Please enter your name");

Außerdem kann mit CSS eine andere Art von Dialogfeld, das sogenannte modale Fenster, implementiert werden. Dieses Dialogfeld deaktiviert das Hauptfenster (lässt es aber sichtbar) und überlagert ein untergeordnetes Pop-up-Fenster, das normalerweise eine Nachricht und einige Schaltflächen anzeigt. Auf der Praxis-Webseite findest du eine Beispielseite mit all diesen Dialogfeldern (Warnung, Bestätigung, Eingabeaufforderung und modales Fenster). Abbildung 4-11 zeigt einen Screenshot dieser Seite, wenn das modale Dialogfeld aktiv ist.

hosw 0411
Abbildung 4-11. Übungswebseite mit Dialogfeldern (Warnung, Bestätigung, Eingabeaufforderung und modal)

Warnungen, Bestätigungen und Eingabeaufforderungen

Die Selenium WebDriver API bietet die Schnittstelle Alert zur Bearbeitung von JavaScript-Dialogen (d.h. Warnungen, Bestätigungen und Eingabeaufforderungen). Tabelle 4-9 beschreibt die Methoden, die von dieser Schnittstelle bereitgestellt werden. Beispiel 4-25 zeigt einen einfachen Test, der mit einem Alert interagiert.

Tabelle 4-9. Alarmmethoden
Methode Rückgabe Beschreibung
accept()
void

Für klicke auf OK

getText()
String

So liest du die Dialogmeldung

dismiss()
void

Auf Abbrechen klicken (nicht in Alerts verfügbar)

sendKeys(String text)
void

So gibst du eine Zeichenfolge in den Eingabetext ein (nur in Eingabeaufforderungen verfügbar)

Beispiel 4-25. Test der Handhabung eines Warndialogs
@Test
void testAlert() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/dialog-boxes.html"); 1
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));

    driver.findElement(By.id("my-alert")).click(); 2
    wait.until(ExpectedConditions.alertIsPresent()); 3
    Alert alert = driver.switchTo().alert(); 4
    assertThat(alert.getText()).isEqualTo("Hello world!"); 5
    alert.accept(); 6
}
1

Wir öffnen die Praxis-Webseite, auf der Dialogfelder geöffnet werden.

2

Wir klicken auf die linke Schaltfläche, um einen JavaScript-Alarm zu starten.

3

Wir warten, bis der Warndialog auf dem Bildschirm angezeigt wird.

4

Wir ändern den Fokus auf das Alarm-Pop-up.

5

Wir überprüfen, ob der Alarmtext den Erwartungen entspricht.

6

Wir klicken auf die Schaltfläche "OK" im Warndialog.

Wir können die Schritte 3 und 4 durch eine einzige explizite Warteanweisung ersetzen, wie folgt (du findest sie in einem zweiten Test in derselben Klasse im Repository für Beispiele):

Alert alert = wait.until(ExpectedConditions.alertIsPresent());

Der nächste Test(Beispiel 4-26) veranschaulicht , wie man mit einem Bestätigungsdialog umgeht. Dieses Beispiel ist dem vorherigen sehr ähnlich, aber in diesem Fall können wir die Methode dismiss() aufrufen, um auf die Schaltfläche Abbrechen zu klicken, die im Bestätigungsdialog verfügbar ist. Beispiel 4-27 schließlich zeigt , wie man eine Eingabeaufforderung verwaltet. In diesem Fall können wir eine Zeichenfolge in den Eingabetext eingeben.

Beispiel 4-26. Test der Handhabung eines Bestätigungsdialogs
@Test
void testConfirm() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/dialog-boxes.html");
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));

    driver.findElement(By.id("my-confirm")).click();
    wait.until(ExpectedConditions.alertIsPresent());
    Alert confirm = driver.switchTo().alert();
    assertThat(confirm.getText()).isEqualTo("Is this correct?");
    confirm.dismiss();
}
Beispiel 4-27. Test der Handhabung einer Eingabeaufforderung
@Test
void testPrompt() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/dialog-boxes.html");
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(5));

    driver.findElement(By.id("my-prompt")).click();
    wait.until(ExpectedConditions.alertIsPresent());
    Alert prompt = driver.switchTo().alert();
    prompt.sendKeys("John Doe");
    assertThat(prompt.getText()).isEqualTo("Please enter your name");
    prompt.accept();
}

Modale Fenster

Modale Fenster sind Dialogfelder, die mit einfachen CSS- und HTML-Funktionen erstellt werden. Aus diesem Grund bietet Selenium WebDriver kein spezielles Dienstprogramm für die Bearbeitung von modalen Fenstern. Stattdessen verwenden wir die Standard-WebDriver-API (Locators, Waits usw.), um mit modalen Fenstern zu interagieren. Beispiel 4-28 zeigt einen einfachen Test mit der Übungswebseite, die Dialogfenster enthält.

Webspeicherung

Die Web Storage API ermöglicht es Webanwendungen , Daten lokal im Dateisystem des Clients zu speichern. Diese API bietet zwei JavaScript-Objekte:

window.localStorage

Um die Daten von dauerhaft zu speichern

window.sessionStorage

Zum Speichern von Daten während der Sitzungszeit (Daten werden gelöscht, wenn der Browser-Tab geschlossen wird)

Selenium WebDriver bietet die Schnittstelle WebStorage für die Handhabung der Web Storage API. Die meisten der WebDriver Typen, die von Selenium WebDriver unterstützt werden, erben diese Schnittstelle: ChromeDriver, EdgeDriver, FirefoxDriver, OperaDriver und SafariDriver. Auf diese Weise können wir diese Funktion dieser Browser nutzen. Beispiel 4-29 demonstriert diese Verwendung in Chrome. In diesem Test werden beide Arten der Webspeicherung (lokal und Sitzung) verwendet.

Beispiel 4-29. Test mit der Webspeicherung
@Test
void testWebStorage() {
    driver.get(
            "https://bonigarcia.dev/selenium-webdriver-java/web-storage.html");
    WebStorage webStorage = (WebStorage) driver; 1

    LocalStorage localStorage = webStorage.getLocalStorage();
    log.debug("Local storage elements: {}", localStorage.size()); 2

    SessionStorage sessionStorage = webStorage.getSessionStorage();
    sessionStorage.keySet()
            .forEach(key -> log.debug("Session storage: {}={}", key,
                    sessionStorage.getItem(key))); 3
    assertThat(sessionStorage.size()).isEqualTo(2);

    sessionStorage.setItem("new element", "new value");
    assertThat(sessionStorage.size()).isEqualTo(3); 4

    driver.findElement(By.id("display-session")).click();
}
1

Wir übergeben das Treiberobjekt an WebStorage.

2

Wir protokollieren die Anzahl der Elemente der lokalen Speicherung.

3

Wir protokollieren die Speicherung der Sitzung (sie sollte zwei Elemente enthalten).

4

Nach dem Hinzufügen eines neuen Elements sollten sich drei Elemente in der Speicherung der Sitzung befinden.

Ereignis-Listener

Die Selenium WebDriver API ermöglicht das Erstellen von Listenern, die Ereignisse in WebDriver und abgeleiteten Objekten melden. In früheren Versionen von Selenium WebDriver war diese Funktion über die Klasse EventFiringWebDriver zugänglich. Diese Klasse ist seit Selenium WebDriver 4 veraltet, und stattdessen sollten wir die folgende Klasse verwenden:

EventFiringDecorator

Wrapper Klasse für WebDriver und abgeleitete Objekte (z.B. WebElement, TargetLocator, etc.). Sie ermöglicht die Registrierung von einem oder mehreren Listenern (d.h. WebDriverListener Instanzen).

WebDriverListener

Schnittstelle, die die im Dekorator registrierten Listener implementieren sollte. Sie unterstützt drei Arten von Ereignissen:

Vor Veranstaltungen

Logik, die kurz vor Beginn eines Ereignisses eingefügt wird

Nach Veranstaltungen

Logik, die kurz nach Beendigung eines Ereignisses eingefügt wird

Fehlerereignisse

Logik eingefügt, bevor eine Ausnahme ausgelöst wird

Um einen Ereignis-Listener zu implementieren, sollten wir zunächst eine Listener-Klasse erstellen. Mit anderen Worten: Wir müssen eine Klasse erstellen, die die WebDriverListener implementiert. Diese Schnittstelle definiert alle ihre Methoden mit dem Schlüsselwort default und daher ist es optional, ihre Methoden zu überschreiben. Dank dieser Funktion (verfügbar ab Java 8) sollte unsere Klasse nur die Methode implementieren, die wir brauchen. Es gibt viele Listener-Methoden, z. B. afterGet() (wird nach dem Aufruf der Methode get() in einer WebDriver -Instanz ausgeführt) oder beforeQuit() (wird vor dem Aufruf der Methode quit() in einer WebDriver -Instanz ausgeführt), um nur einige zu nennen. Um all diese Listener zu überprüfen, empfehle ich dir, deine bevorzugte IDE zu verwenden, um die möglichen Methoden zu entdecken, die überschrieben/implementiert werden müssen. Abbildung 4-12 zeigt den Assistenten für diese Aufgabe in Eclipse.

hosw 0412
Abbildung 4-12. WebDriverListener-Methoden in Eclipse

Sobald wir unseren Listener implementiert haben, müssen wir die Dekoratorklasse erstellen. Es gibt zwei Möglichkeiten, das zu tun. Wenn wir ein WebDriver Objekt dekorieren wollen, können wir eine Instanz von EventFiringDecorator erstellen (und den Listener als Argument an den Konstruktor übergeben) und dann die Methode decorate() aufrufen, um das WebDriver Objekt zu übergeben. Zum Beispiel:

WebDriver decoratedDriver = new EventFiringDecorator(myListener)
        .decorate(originalDriver);

Die zweite Möglichkeit ist, andere Objekte der Selenium WebDriver API zu dekorieren, nämlich WebElement, TargetLocator, Navigation, Options, Timeouts, Window, Alert oder VirtualAuthenticator. In diesem Fall müssen wir die Methode createDecorated() in einem EventFiringDecorator Objekt aufrufen, um eine generische Klasse Decorated<T> zu erhalten. Der folgende Ausschnitt zeigt ein Beispiel mit einem WebElement als Parameter:

Decorated<WebElement> decoratedWebElement = new EventFiringDecorator(
        listener).createDecorated(myWebElement);

Schauen wir uns ein fertiges Beispiel an. Beispiel 4-30 zeigt zunächst die Klasse, die die Schnittstelle WebDriverListener implementiert. Beachte diese Klasse implementiert zwei Methoden: afterGet() und beforeQuit(). Beide Methoden rufen takeScreenshot() auf, um einen Browser-Screenshot zu machen. Alles in allem sammeln wir Browser-Screenshots kurz nach dem Laden einer Webseite (typischerweise zu Beginn des Tests) und vor dem Beenden (typischerweise am Ende des Tests). Beispiel 4-31 zeigt den Test, der diesen Listener verwendet.

Beispiel 4-30. Ereignis-Listener, der die Methoden afterGet() und beforeQuit() implementiert
public class MyEventListener implements WebDriverListener {

    static final Logger log = getLogger(lookup().lookupClass());

    @Override
    public void afterGet(WebDriver driver, String url) { 1
        WebDriverListener.super.afterGet(driver, url);
        takeScreenshot(driver);
    }

    @Override
    public void beforeQuit(WebDriver driver) { 2
        takeScreenshot(driver);
    }

    private void takeScreenshot(WebDriver driver) {
        TakesScreenshot ts = (TakesScreenshot) driver;
        File screenshot = ts.getScreenshotAs(OutputType.FILE);
        SessionId sessionId = ((RemoteWebDriver) driver).getSessionId();
        Date today = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat(
                "yyyy.MM.dd_HH.mm.ss.SSS");
        String screenshotFileName = String.format("%s-%s.png",
                dateFormat.format(today), sessionId.toString());
        Path destination = Paths.get(screenshotFileName); 3

        try {
            Files.move(screenshot.toPath(), destination);
        } catch (IOException e) {
            log.error("Exception moving screenshot from {} to {}", screenshot,
                    destination, e);
        }
    }

}
1

Wir überschreiben diese Methode, um nach dem Laden von Webseiten mit dem WebDriver Objekt eine eigene Logik auszuführen.

2

Wir überschreiben diese Methode, um eine eigene Logik auszuführen , bevor wir das WebDriver Objekt verlassen.

3

Wir verwenden einen eindeutigen Namen für die PNG-Screenshots. Dafür erhalten wir das Systemdatum (Datum und Uhrzeit) plus die Sitzungskennung .

Beispiel 4-31. Test mit EventFiringDecorator und dem vorherigen Listener
class EventListenerJupiterTest {

    WebDriver driver;

    @BeforeEach
    void setup() {
        MyEventListener listener = new MyEventListener();
        WebDriver originalDriver = WebDriverManager.chromedriver().create();
        driver = new EventFiringDecorator(listener).decorate(originalDriver); 1
    }

    @AfterEach
    void teardown() {
        driver.quit();
    }

    @Test
    void testEventListener() {
        driver.get("https://bonigarcia.dev/selenium-webdriver-java/");
        assertThat(driver.getTitle())
                .isEqualTo("Hands-On Selenium WebDriver with Java");
        driver.findElement(By.linkText("Web form")).click(); 2
    }

}
1

Wir erstellen ein dekoriertes WebDriver Objekt mit einer Instanz auf MyEventListener. Wir verwenden das resultierende driver, um den Browser in der @Test Logik zu steuern.

2

Wir klicken auf einen Weblink, um die Seite zu wechseln. Die daraus resultierenden zwei Screenshots, die im Zuhörer aufgenommen wurden, sollten unterschiedlich sein.

WebDriver Ausnahmen

Alle von der WebDriver-API bereitgestellten Ausnahmen erben von der Klasse WebDriverException und sind ungeprüft (siehe die folgende Seitenleiste, wenn du mit dieser Terminologie nicht vertraut bist). Abbildung 4-13 zeigt diese Ausnahmen in Selenium WebDriver 4. Wie diese Abbildung zeigt, gibt es viele verschiedene Ausnahmetypen. Tabelle 4-10 fasst einige der häufigsten Ursachen zusammen.

hosw 0413
Abbildung 4-13. Selenium WebDriver Ausnahmen
Tabelle 4-10. Übliche WebDriver-Ausnahmen und häufige Ursachen
Exception Beschreibung Häufige Ursachen
NoSuchElementException

Webelement nicht verfügbar

  • Ungültige Ortungsstrategie

  • Das Element ist nicht gerendert worden (vielleicht musst du darauf warten)

NoAlertPresentException

Dialog (Warnung, Eingabeaufforderung oder Bestätigung) nicht verfügbar

Der Versuch, eine Aktion (z. B. accept() oder dismiss()) in einem nicht verfügbaren Dialog durchzuführen

NoSuchWindowException

Fenster oder Registerkarte nicht verfügbar

Versuch, zu einem nicht verfügbaren Fenster oder Tab zu wechseln

NoSuchFrameException

Frame oder iframe nicht verfügbar

Der Versuch, in einen nicht verfügbaren Frame oder Iframe zu wechseln

InvalidArgumentException

Falsches Argument beim Aufruf einiger Methoden der Selenium WebDriver API

  • Falsche URL in Navigationsmethoden

  • Nicht vorhandener Pfad beim Hochladen von Dateien

  • Falscher Argumenttyp in einem JavaScript-Skript

StaleElementReferenceException

Das Element ist veraltet, d. h., es erscheint nicht mehr auf der Seite

Das DOM wird aktualisiert, wenn du versuchst, mit einem zuvor gefundenen Element zu interagieren

UnreachableBrowserException

Problem mit der Kommunikation mit dem Browser

  • Die Verbindung mit dem entfernten Browser konnte nicht hergestellt werden

  • Der Browser ist mitten in einer WebDriver-Sitzung abgestürzt

TimeoutException

Zeitüberschreitung beim Laden der Seite

Einige Webseiten brauchen länger als erwartet zum Laden

ScriptTimeoutException

Zeitüberschreitung beim Laden des Skripts

Ein Skript braucht länger als erwartet, um ausgeführt zu werden.

ElementNotVisibleException
ElementNotSelectableException
ElementClickInterceptedException

Das Element befindet sich im DOM, ist aber nicht sichtbar/auswählbar/anklickbar

  • Unzureichende (oder nicht vorhandene) Wartezeit, bis das Element angezeigt/ausgewählt/angeklickt werden kann

  • Das Seitenlayout (vielleicht durch eine Änderung des Ansichtsfensters) führt dazu, dass dieses Element das Element überlagert, mit dem wir interagieren wollen.

Zusammenfassung und Ausblick

In diesem Kapitel hast du auf einen umfassenden Überblick über die WebDriver-API-Funktionen erhalten, die mit verschiedenen Webbrowsern kompatibel sind. Unter anderem hast du erfahren, wie du JavaScript mit Selenium WebDriver ausführen kannst, und zwar mit synchronen, angehefteten (d.h. an eine WebDriver-Sitzung angehängten) und asynchronen Skripten. Dann hast du etwas über Timeouts gelernt, mit denen du ein Zeitlimit für das Laden von Seiten und die Ausführung von Skripten festlegen kannst. Außerdem hast du gesehen, wie du verschiedene Browseraspekte wie Größe und Position, Navigationsverlauf, das Schatten-DOM und Cookies verwalten kannst. Als Nächstes hast du erfahren, wie du mit bestimmten Webelementen interagieren kannst, z. B. mit Dropdown-Listen (Auswahl- und Datenlisten), Navigationszielen (Fenster, Tabs, Frames und Iframes) und Dialogfeldern (Warnungen, Eingabeaufforderungen, Bestätigungen und Modals). Abschließend haben wir uns den Mechanismus zur Implementierung von Webspeicherung und Ereignis-Listenern in Selenium WebDriver 4 sowie die wichtigsten WebDriver-Ausnahmen (und ihre häufigsten Ursachen) angesehen.

Im nächsten Kapitel werden die Funktionen der Selenium WebDriver API weiter erläutert. Das Kapitel erklärt die Aspekte, die für einen bestimmten Browser (z. B. Chrome, Firefox usw.) spezifisch sind, wie z. B. die Browserfunktionen (z. B. ChromeOptions, FirefoxOptions usw.), das Chrome DevTools Protocol (CDP), das Abfangen von Netzwerken, das Mocking von Geolocation-Koordinaten, das WebDriver BiDirectional (BiDi)-Protokoll, Authentifizierungsmechanismen oder das Drucken von Webseiten als PDF und andere Funktionen.

Get Hands-On Selenium WebDriver mit Java now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.