Selbst wenn unsere Anwendung alle Unit-Tests besteht, bedeutet das nicht, dass sie als Gesamtsystem funktioniert. Eine Möglichkeit, um sicherzustellen, dass die Anwendung als Ganzes funktioniert, besteht darin, sie aus der Sicht des Benutzers zu testen. Der einfachste Weg, diese Art von Test durchzuführen, ist das manuelle Testen Ihrer Anwendung. Bei großen Anwendungen kann dies jedoch sehr mühsam und ineffektiv sein, da die Änderung einer Backend-Funktion die anderen beeinträchtigen kann. Hier kommt die Testautomatisierung ins Spiel.

Selenium

Selenium ist eine Tool-Suite, die es ihren Nutzern ermöglicht, In-Browser-Tests zu automatisieren. Sie besteht aus drei Teilen:

  • WebDriver – ein Werkzeug, das mit dem Browser interagiert
  • IDE – ein Browser-Addon, das es Testern ermöglicht, Tests manuell aufzuzeichnen
  • Grid – ein Tool, das die parallele Durchführung von Tests in verschiedenen Umgebungen ermöglicht

Selenium gibt es auch als Bibliothek in einer Vielzahl verschiedener Sprachen, darunter so beliebte Sprachen wie Java, C# und Python. Es ist auch als Teil der ScalaTest + Play-Bibliothek enthalten (da es auf ScalaTest + Selenium-Bibliothek basiert).

Konfiguration

Bevor wir einige einfache Tests erstellen, müssen wir ein paar Dinge konfigurieren. Zunächst müssen wir eine Testspezifikation erstellen, die ein paar Traits erweitert:

  • PlaySpec
  • GuiceOneServerPerSuite
  • OneBrowserPerTest – gewährleistet, dass alle Tests in verschiedenen Browsern durchgeführt werden
  • BeforeAndAfterEach
  • WithInMemoryDatabase – es ist eine benutzerdefinierte Eigenschaft, die die Datenbank im Speicher konfiguriert

Da wir für unsere Tests eine separate Datenbank im Speicher verwenden wollen, müssen wir die MethodefakeApplication() überschreiben, die eine Application Instanz bereitstellt. Wir wollen sie so konfigurieren, dass sie unsere Datenbank verwendet und nicht die Standarddatenbank. Sie sollte in etwa so aussehen:

override def fakeApplication(): Application = {
   GuiceApplicationBuilder()
       .in(Mode.Test)
       .configure("db.default.url" -> database.url, "db.default.driver" -> "org.h2.Driver",
       "db.default.username" -> "", "db.default.password" -> "", "toggle.rule-deployment.log-rule-id" -> true)
       .build()
}

Der database wird von der WithInMemoryDatabase Eigenschaft bereitgestellt. Es ist eine Instanz unserer Datenbank.

Als nächstes werden wir eine Hilfsfunktion implementieren. Sie wird einen Call in einen absoluten URL-String umwandeln. Da wir diese Tests lokal durchführen werden, können wir localhost als Domäne verwenden. Diese Funktion sollte wie die unten dargestellte aussehen.

def absoluteURL(url: Call) = s"http://localhost:$port${url.toString}"

Selenium konfigurieren

Obwohl Selenium mit ScalaTest + Play vorkonfiguriert ist, müssen wir ein paar Anpassungen vornehmen. In dieser Konfiguration verwendet Selenium standardmäßig HtmlUnitDriver, der einige Probleme mit JS Rest Parameters und Bootstrap 5 hat. Dies kann leicht gelöst werden, indem man die Browser-Engine ändert. Wir werden die Chrome-Engine verwenden.

Als erstes müssen wir den WebDriverManager hinzufügen, der den Großteil der Konfiguration für uns übernimmt. Wir müssen seine Abhängigkeit zu build.sbt hinzufügen.

"io.github.bonigarcia" % "webdrivermanager" % "5.2.0"

Als Nächstes müssen wir createWebDriver() überschreiben, das vom Trait OneBrowserPerTest bereitgestellt wird. Unsere Version sollte wie diese aussehen:

 override def createWebDriver(): WebDriver = {
   WebDriverManager.chromedriver().setup()
   val options = new ChromeOptions
   options.addArguments("--headless", "--window-size=1920,1200")
   new ChromeDriver(options)
} 

First line will setup ChromeDriver with the default options. Next we create our own options:

  • --headless – führt Chrome in einem fensterlosen Modus aus
  • --window-size=1920,1200

Erster Test

Nachdem wir nun alles konfiguriert haben, können wir den ersten Test erstellen. Wir werden testen, ob der Benutzer nach einer erfolgreichen Anmeldung auf die Index-Seite umgeleitet wird.

"Login view" must {
   "redirect to index on successful login" in {
       go to absoluteURL(routes.AuthController.login)
       emailField("email").value = users_email
       pwdField("password").value = users_password
       submit()
       eventually(pageTitle mustBe "Main page")
   }
}

Als erstes werden wir die Anmeldeseite go to Um sicherzustellen, dass unser Test auch dann bestanden wird, wenn wir die Anmelde-URL ändern, verwenden wirCall von routes und die zuvor definierte Hilfsfunktion. Als Nächstes suchen wir auf der Seite eine E-Mail-Eingabe mit dem Attribut name=“email“ und füllen sie mit der richtigen E-Mail-Adresse, die einem Benutzer in unserer Testdatenbank entspricht. Als nächstes wollen wir ein Passwortfeld mit dem Attribut name=“password“ finden und es mit dem Passwort des Benutzers füllen. Als nächstes wollen wir das Formular abschicken. Da wir darauf warten müssen, dass dieses Formular vom Server verarbeitet wird, müssen wir eventually(). verwenden. Damit wird versucht, einen Codeblock auszuführen, bis er mit Erfolg beendet wird oder bis eine vordefinierte Zeit verstreicht. In unserem Fall wird der Codeblock prüfen, ob der Titel der Seite gleich „Hauptseite“ ist – denn das ist der Titel unserer Indexseite.

Wir können weitere automatisierte Tests erstellen, die prüfen, ob ein bestimmter Text auf einer Seite vorhanden ist, ob ein Teil die richtige Farbe hat oder ob die paginierte Liste eine genaue Anzahl von Einträgen hat. Da wir dies im Browser tun, testen wir nicht nur, ob alle spezifischen Funktionen korrekt ablaufen, sondern auch, ob alle Ebenen richtig kommunizieren und – was am wichtigsten ist – ob der Benutzer das sieht, was er sehen soll.