5. Selenium

5.1. Einführung

In unserem fünften Projekt haben wir uns mit Selenium und der Webautomatisierung beschäftigt. Selenium ist ein Open-Source-Framework, das Entwicklern ermöglicht, Tests und Skripte für Webanwendungen zu schreiben und auszuführen. Es zählt unter anderem zu den bekanntesten quelloffenen Testwerkzeugen.

Dies bietet eine Reihe von Funktionen und APIs, um Interaktionen mit Webseiten durchzuführen, wie zum Beispiel das Klicken auf Elemente, das Ausfüllen von Formularen, das Navigieren zwischen Seiten oder das Extrahieren von Daten. Es unterstützt verschiedenste Browser wie Chrome, Firefox und Safari.

Selenium basiert rein auf HTML und JavaScript. Für einen schnellen Einstieg kann direkt die Selenium-IDE z. B. als Firefox-Add-on installiert und getestet werden: Der Benutzer interagiert nur mit Firefox, Selenium nimmt den Test auf und spielt ihn wieder ab.

Ein anderer Weg, Selenium-Tests zu erstellen, ist mithilfe der Python-Bibliothek pytest. Dies bietet eine einfache und intuitive Syntax und zahlreiche Funktionen zur Organisation, Durchführung und Berichterstellung von Tests.

5.2. Installation

Um einerseits die Selenium IDE nutzen zu können, muss man diese in der Firefox-Addonbibliothek herunterladen. Jene Bibliothek öffnet man über das Puzzle-Symbol in der oberen rechten Toolbar. Nach dem Download findet man das Addon schließlich ebenfalls unter dem eben genannten Punkt.

Addonbibliothek

Damit man zusätzlich die Selenium-Tests im Nachgang innerhalb der eigenen Python-Umgebung verwenden kann, lassen sich beide Frameworks wie gewohnt über pip installieren.

pip install selenium
pip install pytest

5.3. Aufnahme mit IDE

Als erstes verwendeten wir die Firefox Selenium IDE. Nachdem die IDE geöffnet wurde, lassen sich nun Tests über eine Bildschirmaufnahme erstellen. Hierzu betätigt man ganz einfach den REC-Knopf in der rechten oberen Ecke.

Aufnahme-Start

Sobald die Aufnahme läuft, führt man die gewünschten Aktionen in Firefox aus, in unserem Fall verschieden Aktionen auf der Sportwebseite der Uni Augsburg. Hier wollten wir uns zur Demonstration an einem beliebigen Kurs anmelden.

Kursauswahl

Generell werden alle Aktivitäten im Webbrowser, z.B. Klicken auf Schaltflächen, Ausfüllen von Formularen oder ähnliches, in Selenium-Befehle konvertiert, die den Aktionen entsprechen. Zusätzlich kann man Assertions einfügen, um die erwarteten Ergebnisse zu überprüfen. Außerdem lassen sich die versch. Befehle im Nachhinein noch bearbeiten, erweitern oder anpassen; je nach den unterschiedlichen Anforderungen.

Übersicht
Funktionen der IDE:
  1. Projekt erstellen, öffnen oder speichern

  2. Starten, Pausieren oder Debuggen der Aufnahme

  3. Ausführen + Konfigurieren von Tests (Ausführungsgeschwindigkeit, Ausführen von best. Commands, etc.)

  4. Übersicht der Selenium-Commands mit dem Command-Typ, dem Ziel-Webelement, und einem optionalen Wert, wie z.B. für Formfelder

  5. Durch Auswahl eines Commands lässt sich dieser individuell bearbeiten

Während der Ausführung unseres Tests sind wir jedoch anfangs schnell auf einen Fehler gestoßen, nämlich dass eine best. JavaScript-Funktion von der CSP-Richtlinie blockiert wurde. Dies ist häufig der Fall, um verschiedene Cyber-Attacken abzuwehren, wie z.B. Cross Site Scripting. Hier hieß es im Log:

type on id=BS_F1100 with value Student Failed: call to eval() blocked by CSP
Fehlermeldung

Exportieren des Tests:

Einzelne Tests lassen sich auch innerhalb der IDE exportieren, um sie anderweitig weiterzuverwenden. Hierbei kann man auch versch. Programmiersprachen wählen, die der Test haben soll (C#, Java, Python, Ruby). Wir haben für unseren Fall Python gewählt, um die Test später mithilfe von pytest verwenden zu können.

Export

5.4. Python

Nachdem wir den eben erstellten Test mithilfe der IDE exportiert haben, konnten wir die python-Datei in unser Projekt einbinden. Entweder kann man diesen Code nun nach seinen Bedürfnissen weiter anpassen, und so neue Testfälle erstellen, oder man entwickelt eigene Selenium Tests „from scratch“. Wir haben den erstellten Code mehrmals dupliziert, abgeändert und dadurch in kürzester Zeit mehrere Anwendungstests erstellt.

# Generated by Selenium IDE
import pytest
import time
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class TestIbanfalsch():
  def setup_method(self, method):
    self.driver = webdriver.Firefox()
    self.vars = {}
  
  def teardown_method(self, method):
    self.driver.quit()
  
  def wait_for_window(self, timeout = 2):
    time.sleep(round(timeout / 1000))
    wh_now = self.driver.window_handles
    wh_then = self.vars["window_handles"]
    if len(wh_now) > len(wh_then):
      return set(wh_now).difference(set(wh_then)).pop()
  
  def test_ibanfalsch(self):
    # Test name: iban-falsch
    # Step # | name | target | value
    # 1 | open | https://hsa.sport.uni-augsburg.de/index.php/de/ | 
    self.driver.get("https://hsa.sport.uni-augsburg.de/index.php/de/")
    # 3 | click | linkText=Hier klicken | 
    self.driver.find_element(By.LINK_TEXT, "Hier klicken").click()
    # 4 | click | linkText=G | 
    self.driver.find_element(By.LINK_TEXT, "G").click()
    # 5 | click | linkText=Gaelic-Football | 
    self.driver.find_element(By.LINK_TEXT, "Gaelic-Football").click()
    # 6 | click | name=BS_Kursid_12211 | 
    self.vars["window_handles"] = self.driver.window_handles
    # 7 | selectWindow | handle=${win920} | 
    self.driver.find_element(By.NAME, "BS_Kursid_12211").click()
    # 8 | click | css=label:nth-child(2) > .bs_fval_req | 
    self.vars["win920"] = self.wait_for_window(2000)
    # 9 | click | id=BS_F1100 | 
    self.driver.switch_to.window(self.vars["win920"])
    # 10 | type | id=BS_F1100 | Matthias
    self.driver.find_element(By.CSS_SELECTOR, "label:nth-child(2) > .bs_fval_req").click()
    # 11 | type | id=BS_F1200 | Schugg
    self.driver.find_element(By.ID, "BS_F1100").click()
    # 12 | type | id=BS_F1300 | Hochschulstraße 1
    self.driver.find_element(By.ID, "BS_F1100").send_keys("Matthias")
    # 13 | type | id=BS_F1400 | 86161 Augsburg
    self.driver.find_element(By.ID, "BS_F1200").send_keys("Schugg")
    # 14 | select | id=BS_F1600 | label=StudentIn der HS Augsburg
    self.driver.find_element(By.ID, "BS_F1300").send_keys("Hochschulstraße 1")
    # 15 | click | css=option:nth-child(3) | 
    self.driver.find_element(By.ID, "BS_F1400").send_keys("86161 Augsburg")
    # 16 | type | id=BS_F1700 | 123456789
    dropdown = self.driver.find_element(By.ID, "BS_F1600")
    dropdown.find_element(By.XPATH, "//option[. = 'StudentIn der HS Augsburg']").click()
    # 17 | type | id=BS_F2000 | matthias.schugg@hs-augsburg.de
    # self.driver.find_element(By.CSS_SELECTOR, "option:nth-child(3)").click()
    # 18 | type | id=BS_F2100 | 12345678
    self.driver.find_element(By.ID, "BS_F1700").send_keys("123456789")
    # 19 | type | id=BS_F_iban | DE 23456789876543
    self.driver.find_element(By.ID, "BS_F2000").send_keys("matthias.schugghs-augsburg.de")
    # 20 | click | name=tnbed | 
    self.driver.find_element(By.ID, "BS_F2100").send_keys("12345678")
    # 21 | click | id=bs_submit | 
    self.driver.find_element(By.ID, "BS_F_iban").send_keys("DE 23456789876543")
    self.driver.find_element(By.NAME, "tnbed").click()
    time.sleep(6)
    self.driver.find_element(By.ID, "bs_submit").click()

    email_element = self.driver.find_element(By.XPATH, "/html/body/form/div/div[2]/div/div[15]")
    classlist = email_element.get_attribute('class').split()
    assert 'warn' in classlist

Hier sieht man, dass die Commands der Selenium IDE ganz einfach in python Statements umgewandelt wurden. Die IDE bietet aber hierbei eine übersichtlichere und verständlichere Struktur & Übersicht.

Gestartet können die Tests dann entweder über eine Test-Suite, oder einzeln über folgenden Kommandozeilenaufruf:

pytest test_email_wrong.py

Dadurch werden alle in der Testdatei befindlichen Tests ausgeführt (Funktionen beginnend mit „test_“), wobei der Teststatus und die Ergebnisse in der Konsole sichtbar sind. Wenn man aber doch eine detailliertere Zusammenfassung der Testergebnisse möchte, können versch. Plugins und Erweiterungen zu pytest verwenden. Ein beliebtes Tool ist z.B. pytest-html, mit dem man einen interaktiven HTML-Testbericht generieren kann.

pip install pytest-html

Über pytest und folgendem Befehl lassen sich dann alle Tests ausführen und dazu wird ein passender Bericht geschrieben, der zusätzliche Informationen enthält.

HTML

Es fällt auch sofort auf, das das CSP Problem der IDE hier nicht auftritt, und die gecodete python-Version dieser somit überlegen ist.

5.5. Fazit

Selenium hat sich als äußerst nützliches Werkzeug für die automatisierte Webbrowser-Steuerung und das Testen von Webanwendungen erwiesen. Durch dessen Verwendung lassen sich in kürzester Zeit effiziente, zuverlässige und auch flexible Tests für eine Vielfalt an Use Cases entwickeln. Vor allem die Vorteile im Hinblick auf Vielseitigkeit und Unterstützung verschiedener Programmiersprachen wie Python, Java und C# lassen sich positiv hervorheben. Selenium bietet auch dank der IDE für Firefox eine zugängliches und übersichtliche Lösung für Entwickler mit den unterschiedlichsten Hintergründen. Zusammen mit den verschiedenen kompatiblen Test-Frameworks wie eben pytest und den damit verbundenen zusätzlichen Funktionalitäten bildet Selenium ein effizientes und äußert nützliches Gesamtpaket.

5.6. Quellen

https://www.selenium.dev https://de.wikipedia.org/wiki/Selenium