Beispiel: Unit-Test der Webschicht mit Maven und Arquillian Drone
Inhalt:
Arquillian Drone
Änderungen am Servlet
Code des Tests
Ausführen des Tests
Ausführen mit Java 17
Typ des verwendeten Browsers
Für WildFly 26: hier wird ein Unit-Test für ein EAR-Projekt, bestehend aus EJB-Projekt und Web-Projekt, unter Verwendung des Arquillian-Framework, mittels Maven erstellt und deployed.
Dieses Beispiel basiert komplett auf dem Beispiel Unit-Test mit Maven und Arquillian und erweitert es nur um einen Unit-Test der Webschicht.
Hier gibt es das gepackte Eclipse-Projekt zum Download: StatelessMaven.zip. Die Importanleitung findet man im Stateless Session Bean und Maven-Beispiel.
Arquillian Drone
Arquillian Drone (http://arquillian.org/arquillian-extension-drone/) ist eine Arquillian-Komponente, die für den Test von Webanwendungen genutzt werden kann:
sie erlaubt es, innerhalb des Tests Webseiten aufzurufen, Formulare auszufüllen und abzusenden und HTML-Rückgaben des Servers auszuwerten und zu prüfen.
Arquillian Drone bildet eine Kapsel um den Selenium WebDriver (https://www.seleniumhq.org/). Dieser bildet eine API, um diverse Browser zu steuern, indem Aufrufe über
die Automationsschnittstelle des jeweiligen Browsers geleitet werden.
Doku von Arquillian Drone: https://arquillian.org/arquillian-extension-drone/
Doku des Selenium WebDriver: https://www.seleniumhq.org/docs/03_webdriver.jsp
Damit die vom GeometricModelServlet
berechneten Werte ausgewertet werden können, werden die beiden Ergebnisse "Oberfläche" und "Volumen" in span
-Tags geklammert
und diesen wird ein Name (oder alternativ eine ID) gegeben:
printWriter.write("Volume = <span name=\"volume\">" + dblVolume + "</span>, Surface = <span name=\"surface\">" + dblSurface + "</span>\r\n" + "<br><br><br>\r\n");
Analog zum Beispiel "Unit-Test mit Maven und Arquillian" wird hier ein Integrationstest verwendet.
D.h. im Webprojekt wird im Unterverzeichnis "src\test" die Klasse de.hsrm.cs.javaee8.statelessmaven.web.test.ServletIT
(der Zusatz "IT" markiert sie als Integrationstest) zugefügt:
Sie hat diesen Code:
package de.hsrm.cs.javaee8.statelessmaven.web.test;
import java.net.URL;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.drone.api.annotation.Drone;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
@RunWith(Arquillian.class)
@RunAsClient
public class ServletIT
{
@Deployment
public static Archive<?> getEarArchive()
{
...
}
@Drone
private WebDriver browser;
@ArquillianResource()
private URL deploymentUrl;
@Test
public final void browserTest() throws Exception
{
System.out.println("Testing client website ... " + deploymentUrl);
//Zur Startseite navigieren:
browser.get(deploymentUrl.toString() + "servlet/GeometricModelServlet");
//Formular ausfüllen:
browser.findElement(By.name("a")).sendKeys("7");
browser.findElement(By.name("b")).sendKeys("8");
browser.findElement(By.name("c")).sendKeys("9");
//Formular abschicken:
browser.findElement(By.name("submit")).click();
//Ergebnisse auswerten:
String volume = browser.findElement(By.name("volume")).getText();
Assert.assertEquals("504.0", volume);
String surface = browser.findElement(By.name("surface")).getText();
Assert.assertEquals("382.0", surface);
}
}
Im Detail:
- Die Testklasse erhält zusätzlich die Annotation
org.jboss.arquillian.container.test.api.RunAsClient
: diese bewirkt, dass die Tests dieser Klasse nicht im Server-Container ausgeführt werden, sondern
auf Clientseite.
- Das Erzeugen des Deployment ist identisch zum Beispiel "Unit-Test mit Maven und Arquillian" und ist deshalb hier nicht enthalten.
- Der Test greift über einen
org.openqa.selenium.WebDriver
auf die HTML-Seiten des Servers zu. Eine Instanz dieser Klasse ist als Variable der Testklasse definiert und
wird über die Annotation @org.jboss.arquillian.drone.api.annotation.Drone
von Arquillian Drone injiziert.
- Das Servlet wird über
browser.get(...)
aufgerufen. Hier könnte man die URL des Servlets hartcodiert eintragen:
browser.get("http://localhost:8080/StatelessMaven-web/servlet/GeometricModelServlet");
Besser ist es, die Root-URL der Anwendung von Arquillian injizieren zu lassen. Dazu ist als Klassenmembervariable "deploymentUrl" definiert:
@ArquillianResource()
private URL deploymentUrl;
Die Annotation @org.jboss.arquillian.test.api.ArquillianResource
sorgt dafür, dass Arquillian die Context Root der Anwendung injiziert, also "http://localhost:8080/StatelessMaven-web", siehe
https://arquillian.org/arquillian-core/#arquillian-resource-injection.
Jetzt kann man so auf das Servlet zugreifen:
browser.get(deploymentUrl.toString() + "servlet/GeometricModelServlet");
- Nach Aufrufen der Servlet-Startseite wird das Formular ausgefüllt: die Eingabefelder namens "a", "b", "c" werden bestückt und das Formular durch Klick auf den Button "submit" abgeschickt.
Die jeweiligen Elemente werden durch Aufruf der Methode "findElement" gefunden. Als Parameter wird eine Instanz der Klasse
org.openqa.selenium.By
übergeben, mittels der ein HTML-Element z.B. anhand des Namens
(Methode name
) oder der ID (Methode id
) aufgefunden werden kann.
In ein Eingabefeld können Werte über sendKeys
geschrieben werden.
Der Submit-Button (und damit das Senden des Formulars zum Server) wird durch die Methode "click" ausgelöst.
- Jetzt navigiert der Browser zur Ergebnisseite. Hier werden die Ergebnisse ausgelesen:
String volume = browser.findElement(By.name("volume")).getText();
Ein org.junit.Assert
vergleicht die errechneten Werte mit dem erwarteten Wert.
Fürs Compilieren des Code müssen in "pom.xml" von "StatelessMaven-web" folgende Änderungen durchgeführt werden:
Im Element "dependencies" wird die "arquillian-drone-webdriver-depchain" zugefügt:
<dependencies>
...
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-webdriver-depchain</artifactId>
<type>pom</type>
<scope>test</scope>
</dependency>
</dependencies>
Dies lädt bei WildFly 26 die Version 2.5.2.
Der Wert "test" des Elements "scope" bedeutet, dass diese Abhängigkeit nur zur Compilierung und Ausführung von Testklassen zur Verfügung steht, aber nicht im regulären Code der Anwendung.
Wichtig: danach im Contextmenü "Maven" => "Update Project" aufrufen!
In meinem Beispiel wird über die WildFly-BOM (
https://repo.maven.apache.org/maven2/org/wildfly/bom/wildfly-jakartaee8-with-tools/26.0.0.Final/wildfly-jakartaee8-with-tools-26.0.0.Final.pom)
schon die entsprechende Abhängigkeit bereitgestellt. Wäre das nicht der Fall, müsste man
im Element "dependencyManagement" die "Bill of Materials" von Drone zufügen, hier in der Version "2.5.2" - man sollte darauf achten dass sie zu der Arquillian-Version kompatibel ist:
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-drone-bom</artifactId>
<version>2.5.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Ausführen des Tests
Siehe Beispiel Unit-Test mit Maven und Arquillian.
Da ich hier zwei Integrationstests habe, wird die EAR-Datei zweimal auf den WildFly-Server deployed und wieder entfernt.
Ausführen mit Java 17
Auch hier sei auf das Beispiel Unit-Test mit Maven und Arquillian verwiesen.
Typ des verwendeten Browsers
Der Selenium WebDriver verwendet in der Defaulteinstellung HtmlUnit (http://htmlunit.sourceforge.net/) - ein "Browser" ohne GUI,
wie man an der Logausgabe bei der Testausführung sieht:
Jan. 16, 2022 12:23:49 NACHM. org.jboss.arquillian.drone.webdriver.factory.AbstractWebDriverFactory createConfiguration
INFO: Property "browser" was not specified, using default value of htmlunit
In der Datei "arquillian.xml" kann man auf einen anderen Browser umschalten:
<arquillian ...>
<extension qualifier="webdriver">
<property name="browser">firefox</property>
</extension>
</arquillian ...>
Im Beispiel wird Firefox verwendet. Bei Ausführung des Tests sieht man in der Konsole, dass ein "geckodriver" heruntergeladen und entpackt wird:
Drone: downloading geckodriver-v0.23.0-win64.zip from https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-win64.zip to C:\Users\USERHOME\.arquillian\drone\firefox\v0.23.0\geckodriver-v0.23.0-win64.zip
................................................................................
Okt 15, 2018 8:46:12 PM org.jboss.arquillian.drone.webdriver.binary.BinaryFilesUtils extract
INFORMATION: Extracting zip file: C:\Users\USERHOME\.arquillian\drone\firefox\v0.23.0\geckodriver-v0.23.0-win64.zip to C:\Temp\workspace\StatelessMaven\StatelessMaven-web\target\drone\1458a83f4cb051132fe9a91f30770162
Stand 16.01.2023
Historie:
18.10.2018: erstellt
07.01.2019: Update auf WildFly 15
16.02.2019: diverse Plugins aktualisiert
25.01.2020: Projekt auf Basis des Archetype "wildfly-jakartaee-ear-archetype" erstellt - dadurch weniger Nacharbeiten an "pom.xml" nötig, Profile "arq-managed" startet jetzt Server aus "JBOSS_HOME"-Variable, WildFly 18
16.01.2022: WildFly 26, Links aktualisiert
16.01.2023: Hinweis zu Fehler bei Java 17