Erste Schritte

Author: Dan Allen Translators: Markus Eisele, Bennet Schulz Language:
Tags: cdi, weld, maven, forge, eclipse Last Update:Sep 21, 2017

Dieser Guide stellt Arquillian vor. Nach dem Lesen dieses Guides kannst Du:

  • Arquillian zu einem Maven-basierten Projekt hinzufügen
  • einen Arquillian-Testfall schreiben, der das Verhalten eines CDI- (Context and Dependency Injection) Beans überprüft.
  • den Arquillian-Testfall in verschiedenen Containern ausführen.

Du wirst all dies lernen, indem du Arquillian als Testsuite zu einem Java-EE-basierten Maven-Projekt hinzufügst. Wir haben diesen Guide so entworfen, dass du ihn schnell durcharbeiten und sofort starten kannst!

Annahmen

Der einfachste Weg mit Arquillian anzufangen ist, wenn man es in ein Projekt integriert, welches bereits Abhängigkeitsmanagement (Dependency Management) unterstützt. Das am weitesten Verbreitete Werkzeug dieser Kategorie heutzutage ist Apache Maven. Dieser Guide führt Dich zu Deiner ersten green bar (grüner Testbalken) mithilfe eines Beispiel-Maven-Projekts.

Arquillian basiert nicht auf Maven oder irgendeinem anderen Build-Tool. Es funktioniert genauso gut — wenn nicht besser - wenn es in einem Projekt verwendet wird, welches einen Ant oder Gradle-Build einsetzt. Idealerweise sollte das Build-Werkzeug allerdings Dependency Management anbieten, da es die Integration der Arquillian-Bibliotheken deutlich vereinfacht, weil diese bereits im Maven Central repository vorliegen.

Dieser Guide setzt voraus, dass bei Dir Maven installiert ist: Entweder als Kommandozeilenwerkzeug oder in Deiner IDE (Integrated Development Environment). Wenn nicht, bitte installiere Maven jetzt. Du wirst auch ein JDK auf Deinem Rechner benötigen.

Erstelle ein neues Projekt

Es gibt zwei von uns empfohlene Wege ein neues Maven-Projekt zu erstellen:

  1. Erstelle ein Projekt von einem Maven-Archetype
  2. Erstelle und passe ein Projekt mit JBoss Forge an

Die Verwendung von JBoss Forge stellt den mit Abstand einfacheren Weg dar. Dieser Guide bietet dir allerdings beide Alternativen an, für den Fall, dass du noch nicht bereit bist JBoss Forge einzusetzen. Wähle eine der obigen Optionen, um zur jeweiligen Anleitung zu springen.

Wenn Du bereits ein Maven-basiertes Projekt hast, kannst Du diese Sektion als Referenz verwenden, um sicherzustellen, dass Du die richtigen Abhängigkeiten im Projekt hast, bevor Du weitermachst.

Erstelle ein Projekt von einem Maven-Archetype aus

Zuerst erstelle ein Maven-basiertes Java-Projekt mithilfe der Kommandozeile unten.

$ mvn archetype:generate -DarchetypeGroupId=net.avh4.mvn.archetype \
-DarchetypeArtifactId=java-1.6-archetype

Kopiere den Text nach dem $ und füge ihn in deine Kommandozeilen-Fenster ein. Füge, wenn Du dazu aufgefordert wirst, die in der folgen Liste stehenden Werte ein und bestätige sie mit <ENTER>.

Define value for property 'groupId': : org.arquillian.example <ENTER>
Define value for property 'artifactId': : arquillian-tutorial <ENTER>
Define value for property 'version': : <ENTER>
Define value for property 'package': : <ENTER>
Confirm properties configuration:
groupId: org.arquillian.example
artifactId: arquillian-tutorial
version: 1.0-SNAPSHOT
package: org.arquillian.example
Y: : <ENTER>

Dieses Kommando erzeugt ein Maven-basiertes Java-Projekt innerhalb eines Verzeichnisses mit dem Namen arquillian-tutorial unterhalb des aktuellen Verzeichnisses. Die Dateistruktur des Projekt ist wie folgt:

  • src/
    • main/
      • java/ – Hier kommen alle Java-Sourcedateien hin (in Java-Packages)
      • resources/ – Hier kommen alle Konfigurationsdateien der Anwendung hin.
    • test/
      • java/ – Hier kommen alle Test-Java-Sourcedateien hin (in Java-Packages)
      • resources/ – Hier kommen alle Konfigurationsdateien für die Tests hin (beispielsweise: arquillian.xml)
  • pom.xml – Die Maven-Builddatei, welche Maven sagt, wie das Projekt zu bauen ist.

Das erzeugte Projekt ist mit Java 1.6 und JUnit 4.8 vorkonfiguriert. Dies sind die im Minimum benötigten Versionen von Java und JUnit, um Arquillian zu nutzen.

Der Generator hat auch ein Java-Package mit dem Namen org.arquillian.example unterhalb der beiden java-Verzeichnisse erstellt. Dorthin solltest Du auch Deine Java-Sourcen packen und nicht direkt in das Root der java-Verzeichnisse.

Arquillian unterstützt auch TestNG 5. Dennoch verwendet dieser Guide JUnit als Beispiel.

Öffnet als nächstes das pom.xml in Deiner IDE oder einem Editor. Du solltest eine XML-Datei mit den Basisprjektiformationen, einer <build>-und einer <dependencies>-Section sehen. Du kannst alle <dependency>-Elemente unterhalb der JUnit-Abhängigkeit entfernen. Sie werden nicht benötigt.

Wenn Du fertig bist, sollte das Ergebnis ungefähr so aussehen (abgekürzt zur besseren Lesbarkeit):

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://maven.apache.org/POM/4.0.0
        http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.arquillian.example</groupId>
    <artifactId>arquillian-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>arquillian-tutorial</name>
    <url>http://arquillian.org</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Wir werden Java-EE-7-Komponenten schreiben. Daher benötigst Du außerdem die Java-EE-7-API als abhängige Bibliothek im Classpath, um diese kompilieren zu können.

Öffne erneut die pom.xml-Datei und füge das folgende XML-Fragment direkt innerhalb der umschließenden <dependencies>-Elemente ein. Das sollte dann so aussehen, wenn Du fertig bist:

pom.xml
<!-- clip -->
<dependencies>
    <dependency>
        <groupId>org.jboss.spec</groupId>
        <artifactId>jboss-javaee-7.0</artifactId>
        <version>1.0.3.Final</version>
        <type>pom</type>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>
<!-- clip -->

Die Grundlagen für Dein Projekt sind jetzt fertig! Überspringe den nächsten Abschnitt und gehe direkt zu Projekt in Eclipse öffnen, damit wir endlich Code schreiben können!

Erstelle und passe ein Projekt mit JBoss Forge an

JBoss Forge ist ein Kommandozeilenwerkzeug zum Rapid Application Development in einer auf Standards basierenden Umgebung. Gerne auch als “Maven Archetypes on steroids” bezeichnet.

Forge ist schnell installiert und dieser Guide führt Dich durch die Grundlagen. Folge einfach diesen einfachen Schritten, um es zu installieren:

  1. Download Forge und packe Forge in ein beliebiges Verzeichnis auf Deiner Festplatte aus. Diese nennen wir $FORGE_HOME
    Wir gehen davon aus, dass Du die Distribution in ein Verzeichnis mit dem Namen forge in Deinem Home-Verzeichnis ausgepackt hast.
  1. Füge $FORGE_HOME/bin zu Deinem Pfad hinzu (Windows, Linux oder Mac OSX)

Auf Unix-basierenden Systemen bedeutet dies, dass Du $HOME/.bashrc or $HOME/.profile editieren musst. Eine entsprechende Ergänzung sieht dann beispielsweise so aus:

$ export FORGE_HOME=$HOME/forge/
$ export PATH=$PATH:$FORGE_HOME/bin

Unter Windows kann dies über die Systemeigenschaften eingestellt werden. Dorthin gelangst Du mit einem Rechtsklick auf die “Systemsteuerung”, dann Klick auf “Systemeigenschaften”, dort der Reiter “Erweitert”. Ein Klick auf “Umgebungsvariablen” ermöglicht das Hinzufügen der beiden Einträge. Es wird empfohlen die Einträge als Nutzervariablen vorzunehmen. Systemvariablen ist nur sinnvoll, wenn Forge in einem Verzeichnis installiert ist, welches von allen Systembenutzern gelesen werden kann.

Nachdem Forge installiert (bzw. entpackt) wurde, öffne die Kommandozeile und führe das folgende forge-Kommando aus:

$ forge
   _____
  |  ___|__  _ __ __ _  ___
  | |_ / _ \| `__/ _` |/ _ \  \\
  |  _| (_) | | | (_| |  __/  //
  |_|  \___/|_|  \__, |\___|
                  |___/

[no project] ~ $

Das war es schon! Forge läuft und es wird Zeit, das Projekt zu erstellen.

Tippe das folgende Kommando innerhalb der Forge-Shell. Es erzeugt ein leeres Projekt in etwa der Art, wie es der Maven-Archetype auch getan hat:

$ project-new --named arquillian-tutorial --topLevelPackage org.arquillian.example

Dieses Kommando erzeugt ein Maven-basiertes Java-Projekt innerhalb eines arquillian-tutorial genannten Verzeichnisses direkt unterhalb des aktuellen Verzeichnisses.

Die Dateistruktur ist dabei wie folgt erzeugt worden:

  • src/
    • main/
      • java/ – Hier kommen alle Java-Sourcedateien hin (in Java-Packages)
      • resources/ – Hier kommen alle Konfigurationsdateien der Anwendung hin.
        • META-INF/
          • forge.xml – Eine leere Forge Settings Datei
    • test/
      • java/ – Hier kommen alle Test-Java-Source-Dateien hin (in Java-Packages)
      • resources/ – Hier kommen alle Konfigurationsdateien für die Tests hin (beispielsweise: arquillian.xml)
  • pom.xml – Die Maven-Builddatei, welche Maven sagt, wie das Projekt zu bauen ist.

Forge wechselt automatisch in das erstellte Projektverzeichnis.

[arquillian-tutorial] arquillian-tutorial $

Nun müssen die Java-EE-APIs hinzugefügt werden. Das wird mit dem project-add-dependency-Kommando gemacht:

$ project-add-dependencies org.jboss.spec:jboss-javaee-7.0:1.0.3.Final:pom:provided

Dann fehlt noch JUnit 4.12 für die Tests, als weitere Minimalanforderung von Arquillian.

$ project-add-dependencies junit:junit:4.12:test

Im Ergebnis sieht die von Forge erzeugte pom.xml dann so aus:

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.arquillian.tutorial</groupId>
    <artifactId>arquillian-tutorial</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.jboss.spec</groupId>
          <artifactId>jboss-javaee-7.0</artifactId>
          <version>1.0.3.Final</version>
          <type>pom</type>
          <scope>provided</scope>
        </dependency>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.jboss.spec</groupId>
        <artifactId>jboss-javaee-7.0</artifactId>
        <type>pom</type>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
      </dependency>
    </dependencies>
    <build>
      <finalName>arquillian-tutorial</finalName>
      <plugins>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>2.4</version>
          <configuration>
            <failOnMissingWebXml>false</failOnMissingWebXml>
          </configuration>
        </plugin>
      </plugins>
    </build>
</project>

Arquillian kann über das Maven Central Repository bezogen werden. Daher ist der Verweis auf das JBoss Public Repository in der pom.xml überflüssig und kann entfernt werden. Behalte aber im Hinterkopf, dass es durchaus abhängige Bibliotheken von JBoss gibt, welche nicht über Maven Central bezogen werden können.

Wenn Du zu den Anhängern gehörst, welche das Hinzufügen von Repositories in Projekt pom.xml für ein Anti-Pattern halten, dann lies diese Anweisungen, um die Repositories global in der Maven-settings.xml-Datei einzustellen.

Die Grundlagen für Dein Projekt sind jetzt fertig! Öffne das Projekt in Eclipse, damit wir endlich Code schreiben können!

Projekt in Eclipse öffnen

Beim Entwickeln eines Java-Projektes wirst Du vermutlich eine IDE, beispielsweise Eclipse, verwenden. Daher wurde Arquillian auch IDE-freundlich konzipiert. Das bedeutet, dass Arquillian Tests aus der IDE ohne besondere Änderungen ausführbar sind. Darum starten wir auch gleich mit der IDE:

Starte Eclipse. Da dies ein Maven-basiertes Projekt ist, benötigst Du die Maven Integration for Eclipse, auch m2e-Plugin genannt, um das Projekt zu öffnen. Hast Du diese nicht bereits installiert, kannst Du auch einfach die JBoss Tools installieren. Sie enthalten bereits die Maven-Integration. Die folgenden Schritte beschreiben die Installation direkt über den Eclipse Marketplace (eine Art AppStore für Eclipse-Plugins).

  1. Wähle das Menü Hilfe > Eclipse Marketplace... vom Hauptmenü
  2. Tippe “jboss tools” in das Eingabefeld (ohne Anführungszeichen) und drücke <ENTER>
  3. Klicke auf den “Installieren”-Knopf neben dem Ergebnis “JBoss Tools (Indigo)”
  4. Beende den Install Wizard und starte Eclipse neu, wenn Du dazu aufgefordert wirst.

Bei den JBoss Tools handelt es sich um ein leichtgewichtiges Plugin, welches ein umfangreiches Set an Werkzeugen zur Entwicklung von Java-EE-Anwendungen inklusive CDI-Unterstützung bereitstellt. Um produktiv arbeiten zu können, empfehlen wir es daher, dieses Plugin zu verwenden.

Wenn du darauf allerdings verzichten möchtest und lieber nur die Maven-Integration ohne Plugins verwenden möchtest, folge diesen einfachen Schritten:

  1. Wähle das Menü Hilfe > Eclipse Marketplace... vom Hauptmenü
  2. Tippe “maven” in in das Eingabefeld (ohne Anführungszeichen) und drücke <ENTER>
  3. Klicke auf den “Installieren”-Knopf neben dem Ergebnis “Maven Integration for Eclipse”
  4. Beende den Install Wizard und starte Eclipse neu wenn Du dazu aufgefordert wirst
  5. Wiederhole die Schritte für die “Maven Integration für Eclipse WTP”

Ist das Maven-Plugin installiert, kann man mit einfachen Schritten das Projekt direkt öffnen:

  1. Wähle Datei > Import... vom Hauptmenü
  2. Tippe “existing maven” in in das Eingabefeld
  3. Wähle die Option “Existing Maven Projects”, dann klicke auf den Weiter-Knopf
  4. Klicke den “Durchsuchen ….” Knopf
  5. Navigiere zum Projektverzeichnis in Deinem Dateisystem, dann Klicke den OK-Knopf.
  6. Klicke auf “Fertig” um das Projekt zu öffnen

Eclipse erkennt dann das Maven-Projekt und öffnet es in der Project Navigator View. Wenn Du das Projekt aufklappst, sollte es folgendermaßen aussehen:

Jetzt kann es aber wirklich losgehen!

Eine Komponente erstellen

Um einen Arquillian-Test schreiben zu können, benötigen wir erst einmal eine Komponente, welche es zu testen gibt. Wir fangen mit einer einfachen Komponente an, damit Du lernen kannst, wie ein einfach Arquillian-Test ausgeführt wird. In den nächsten Schritten geht es zu deutlich komplexeren Szenarien.

Erstelle mithilfe Deiner IDE eine neue Java-Klasse mit dem Namen Greeter im org.arquillian.example-Package. Ersetze den Inhalt mit dem folgenden Code:

src/main/java/org/arquillian/example/Greeter.java
package org.arquillian.example;

import java.io.PrintStream;

/**
 * A component for creating personal greetings.
 */
public class Greeter {
    public void greet(PrintStream to, String name) {
        to.println(createGreeting(name));
    }

    public String createGreeting(String name) {
        return "Hello, " + name + "!";
    }
}

Im Test wollen wir sicherstellen, dass diese Klasse sich richtig verhält, wenn sie als CDI (Contexts and Dependency Injection) Bean aufgerufen wird. Natürlich könnten wir jetzt auch einen einfachen Unit-Test schreiben, aber lass uns einfach annehmen, dass diese Bean weitere Enterprise Services wie beispielsweise Dependency Injection und Messaging nutzt und in einem Container ausgeführt werden muss. (Und außerdem hat es so Raum zu wachsen ~;))

Um diese Klasse als CDI Bean zu benutzen, injizieren wir sie mithilfe der @Inject-Annotation in den Test. Das schreit doch geradezu nach einem Arquillian-Test! Nun wird es also Zeit, die Arquillian-API zum Projekt hinzuzufügen!

Arquillia-APIs hinzufügen

Öffne die pom.xml ein weiteres Mal im Editor. (Erinnerung: Sie liegt im Rootverzeichnis Deines Projektes). Jetzt muss Maven wissen, welche Version der Abhängigkeiten es verwenden soll. Dazu wird das folgende XML-Fragment direkt oberhalb des <build>-Elements eingefügt, um den sogenannten BOM (Bill of Materials) bzw. die Versionsmatrix für alle transitiven Abhängigkeiten von Arquillian zu importieren.

pom.xml
<!-- clip -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian</groupId>
            <artifactId>arquillian-bom</artifactId>
            <version>1.4.0.Final</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>
<!-- clip -->

Nun muss die Arquillian-JUnit-Integration als Abhängigkeit ergänzt werden. Dazu gehört dieses XML-Fragment unter das letzte geschlossene <dependency>-Element:

pom.xml
<!-- clip -->
<dependency>
    <groupId>org.jboss.arquillian.junit</groupId>
    <artifactId>arquillian-junit-container</artifactId>
    <scope>test</scope>
</dependency>
<!-- clip -->

Die Arquillian-JUnit-Integration fügt auch die Arquillian- und die ShrinkWrap-APIs zum Test-Classpath hinzu. Alle diese Bibliotheken werden benötigt, um einen Arquillian-Test schreiben und kompilieren zu können.

Um statt JUnit TestNG zu verwenden, muss die Arquillian-JUnit-Integration mit der Arquillian-TestNG-Integration ersetzt werden.

Wenn Du Probleme mit dem bisher erstellten pom.xml hast, dann kannst Du eine komplette Version von diesem gist herunterladen.

Jetzt ist alles vorbereitet, um Deinen ersten Arquillian-Test zu schreiben!

Einen Arquillian-Test schreiben

Ein Arquillian-Test sieht erst mal so aus wie ein herkömmlicher Unittest. Allerdings mit etwas mehr Flair. Zurück zu Deiner IDE:

Wenn Du die Meldung “Project configuration is out of date with pom.xml” erhälst, dann klicke rechts auf das Projekt und wähle Project > Maven > Update Project Configuration, um das Projekt neu zu synchronisieren.

Starten wir mit dem Erstellen eines neuen JUnit-Testfalls mit dem Namen GreeterTest im Verzeichnis src/test/java unterhalb des org.arquillian.example-Packages. Du wirst die typischen setUp()- und tearDown()- Methoden nicht benötigen, da Arquillian das meiste der harten Arbeit für Dich übernimmt. Das hier haben wir bisher:

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

import org.junit.Assert;
import org.junit.Test;

public class GreeterTest {
    @Test
    public void should_create_greeting() {
        Assert.fail("Not yet implemented");
    }
}

Nun zum Flair. Ein Arquillian-Testfall muss drei Dinge beinhalten:

  1. Die @RunWith(Arquillian.class)-Annotation an der Klasse
  2. Eine statische Methode annotiert mit @Deployment, welche ein ShrinkWrap-Archiv zurückgibt
  3. Und mindestens eine Methode, welche mit @Test annotiert ist

Die @RunWith-Annotation sagt JUnit, dass es den Arquillian-Testcontroller verwenden soll. Arquillian schaut dann nach der statischen Methode mit der @Deployment-Annotation und empfängt das Testarchiv (auch micro-deployment genannt). Dann passiert ein gewisses Maß an Magie und jede mit @Test annotierte Methode wird innerhalb der Containerumgebung ausgeführt.

Was genau ist ein Testarchiv?

Der Zweck eines Testarchivs ist die Isolation von Klassen und Ressourcen, welche für den Test benötigt werden, von dem Rest des Classpath. Im Gegensatz zu einem normalen Unittestfall bindet sich Arquillian nicht einfach in den kompletten Classpath ein. Im Gegenteil, Du fügst lediglich die Teile hinzu, welche auch vom Test benötigt werden (das kann der komplette Classpath sein, aber das entscheidest nur Du). Das Archiv wird per ShrinkWrap definiert. Das ist eine Java-API zur Erzeugung von Archiven (beispielsweise jar, war, ear). Die Micro-deployment-Strategie erlaubt es Dir, Dich auf die eigentlichen Testklassen zu fokussieren. Im Ergebnis führt das zu sehr schlanken Testklassen.

ShrinkWrap unterstützt auch das Auffinden von Artefakten (Bibliotheken) und das programmatische Erstellen von Konfigurationsdateien, welche ebenfalls im Testarchiv verpackt werden können. Für eine gründlichere Einführung in ShrinkWrap empfiehlt sich der ShrinkWrap introduction guide.

Fügen wir das Arquillian-Flair dem Test hinzu:

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class GreeterTest {

    @Deployment
    public static JavaArchive createDeployment() {
        return ShrinkWrap.create(JavaArchive.class)
            .addClass(Greeter.class)
            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Test
    public void should_create_greeting() {
        Assert.fail("Not yet implemented");
    }
}

Mithilfe von ShrinkWrap haben wir ein Java-Archiv (jar) als Deployment-Artefakt definiert. Es enthält die zu testende Greeter-Klasse und eine leere beans.xml-Datei, welche als Ressource im META-INF-Verzeichnis liegen soll. Mit diesem Trick aktivieren wir die CDI-Funktionen für dieses Archiv.

Alles was nun noch zu tun bleibt, ist, die Greeter-Instanz in ein Feld direkt oberhalb der Testmethode zu injizieren und die bisher nicht implementierte Testmethode mit einem Assert auf das Verhalten des Beans auszuprogrammieren.
Um Dir ein warmes und wohliges Gefühl zu vermitteln, werden wir auch die Begrüßung auf der Konsole ausgeben.

src/test/java/org/arquillian/example/GreeterTest.java
// clip
import javax.inject.Inject;
// clip

@Inject
Greeter greeter;

@Test
public void should_create_greeting() {
    Assert.assertEquals("Hello, Earthling!",
        greeter.createGreeting("Earthling"));
    greeter.greet(System.out, "Earthling");
}

Und so sieht dann der fertige Test aus:

src/test/java/org/arquillian/example/GreeterTest.java
package org.arquillian.example;

import javax.inject.Inject;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.Assert;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class GreeterTest {

    @Deployment
    public static JavaArchive createDeployment() {
        return ShrinkWrap.create(JavaArchive.class)
            .addClass(Greeter.class)
            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
    }

    @Inject
    Greeter greeter;

    @Test
    public void should_create_greeting() {
        Assert.assertEquals("Hello, Earthling!",
            greeter.createGreeting("Earthling"));
        greeter.greet(System.out, "Earthling");
    }
}

Glückwunsch! Damit hast Du gerade Deinen ersten Arquillian-Test geschrieben!

Ahhh. Aber Du wunderst Dich vielleicht darüber, wie Du ihn ausführen sollst? ~:S Wenn Du denkst, “Einfach wie einen JUnit-Test”, liegst Du genau richtig! Dennoch müssen wir zuerst einen Container-Adapter zum Classpath hinzufügen.

Einen Container-Adapter hinzufügen

Wir haben eine Menge über Tests innerhalb irgendeines Containers gesprochen, bisher aber nicht gesagt, welchen Container wir verwenden. Das liegt daran, dass es eine Laufzeitentscheidung ist.

Arquillian wählt den Ziel-Container auf der Grundlage des im Classpath verfügbaren Container-Adapters aus. Das bedeutet, dass wir weitere Bibliotheken zum Projekt hinzufügen werden.

Ein Arquillian-Test kann in allen Containern ausgeführt werden, welche mit dem Programmiermodell des Tests kompatibel sind und für die Arquillian einen entsprechenden Container-Adapter bereitstellt. Der bisherige Test verwendet das CDI-Programmiermodell; wir benötigen also einen Container, welcher dieses unterstützt. Wir wollen einen schnellen Roundtrip und starten daher mit dem Weld EE embedded Container.

Öffne wieder die Projektkonfiguration pom.xml und füge die folgende Gruppe von Abhängigkeiten unterhalb des letzten, schließenden <dependency>-Elements hinzu:

pom.xml
<!-- clip -->
<dependency>
    <groupId>org.jboss.arquillian.container</groupId>
    <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
    <version>1.0.0.CR9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.jboss.weld</groupId>
    <artifactId>weld-core</artifactId>
    <version>2.3.5.Final</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.6.4</version>
    <scope>test</scope>
</dependency>
<!-- clip -->

Zusammenfassend benötigst Du diese drei Bibliotheken, um Arquillian mit JUnit zu verwenden:

  1. Die Arquillian-JUnit-Integration
  2. Einen Arquillian-Container-Adapter für den gewünschten Zielcontainer
  3. Die Container-Laufzeit (für einen embedded container) oder einen Container-Client (für einen remote container)

Wir verwenden einen sogenannten “embedded container” in diesem Beispiel. Das bedeutet, wir benötigen die Container-Laufzeitumgebung: Also Weld.

Aber zurück zum Test.

Den Arquillian-Test ausführen

Sind alle notwendigen Abhängigkeiten bzw. Bibliotheken dem Classpath hinzugefügt, kann der Arquillian-Test genau wie ein Unittest ausgeführt werden; egal ob durch die IDE oder das Buildscript oder andere Build-Plugins. Wir führen den Beispieltest in Eclipse aus.

Im IDE-Fenster klicke mit der rechten Maustaste auf die GreeterTest.java-Datei im Eclipse Package Explorer (oder im Editor) und wähle “Run As > JUnit Test” aus dem Kontextmenü.

Bei der Ausführung des Tests solltest Du die folgende Ausgabe auf der Konsole sehen:

21 [main] INFO org.jboss.weld.Version - WELD-000900 2.3.5 (Final)
Hello, Earthling!

Dann sollte die JUnit View erscheinen und eine green bar anzeigen!

Auch direkt über Maven von der Kommandozeile kann der Test ausgeführt werden:

$ mvn test

Die nachfolgende Ausgabe sollte dabei auf der Konsole erscheinen:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.arquillian.example.GreeterTest
19 [main] INFO org.jboss.weld.Version - WELD-000900 2.3.5 (Final)
Hello, Earthling!
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.907 sec

Herzlichen Glückwunsch! Du hast Dir grade Deine erste green bar mit Arquillian verdient!

Genauer hingeschaut!

Wie kannst Du wissen, dass CDI wirklich funktioniert? Nach allem, was Du weißt, hat Arquillian eine neue Instanz der Greeter-Klasse erzeugt und in den Test ohne weitere Beteiligung von CDI injiziert. Lass uns beweisen, dass sie wirklich da ist.

Erstelle eine neue CDI-Bean mit dem Namen PhraseBuilder im org.arquillian.example-Package. Diese soll Sätze aus Vorlagen erstellen können.

src/main/java/org/arquillian/example/PhraseBuilder.java
package org.arquillian.example;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;

public class PhraseBuilder {
    private Map<String, String> templates;

    public String buildPhrase(String id, Object... args) {
        return MessageFormat.format(templates.get(id), args);
    }

    @PostConstruct
    public void initialize() {
        templates = new HashMap<String, String>();
        templates.put("hello", "Hello, {0}!");
    }
}

Öffne nun die Greeter-Klasse und erstelle einen neuen Konstruktor, welcher eine PhraseBuilder-Instanz über Constructor Injection erzeugt. Dann delegiere die Erzeugung der Begrüßung an die injizierte Bean.

src/main/java/org/arquillian/example/Greeter.java
package org.arquillian.example;

import java.io.PrintStream;
import javax.inject.Inject;

public class Greeter {

    private PhraseBuilder phraseBuilder;

    @Inject
    public Greeter(PhraseBuilder phraseBuilder) {
        this.phraseBuilder = phraseBuilder;
    }

    public void greet(PrintStream to, String name) {
        to.println(createGreeting(name));
    }

    public String createGreeting(String name) {
        return phraseBuilder.buildPhrase("hello", name);
    }
}

Damit der Test wirklich funktioniert, muss jetzt also eine Instanz der Klasse PhraseBuilder erstellt werden, seine @PostConstruct Methode ausgeführt und in den Constructor der Greeter Instanz injiziert werden, sobald eine Greeter-Instanz erzeugt wird. Wir können sicher sein, dass CDI am Werk ist, wenn all dies zusammenspielt.

Ein letzter Schritt: Da wir eine neue Klasse erstellt haben, müssen wir zuerst sicherstellen, dass diese in der @Deployment-Methode des Tests dem Archiv hinzugefügt wird. Dazu verändern wir einfach die folgende Zeile von:

.addClass(Greeter.class)

…nach:

.addClasses(Greeter.class, PhraseBuilder.class)

Lass den Test erneut ablaufen. Du solltest erneut eine green bar! sehen! Fühlt sich gut an, nicht wahr?

Den Test debuggen

Das wird ein kurzes Kapitel. Warum? Weil Arquillian-Tests so unkompliziert sind, dass Du sie genauso wie alle anderen Unittests debuggen kannst. Füge einfach irgendwo einen Breakpoint hinzu (entweder im Test-Code oder im Anwendungs-Code) und klicke mit der rechten Maustaste oder im Editor auf den Test und wähle “Debug As > JUnit Test”. Jetzt debuggst Du im Container!

Wenn Du einen Remote Container verwendest, funktioniert das so nicht. Stattdessen muss der Container im Debug Mode gestartet und der Debugger angehängt werden. Der Test läuft dann nämlich in einer anderen JVM als der eigentliche Code.

Du hast grade erlebt, dass Arquillian das ideale Werkzeug für das Testen von CDI-Anwendungen ist. Es kümmert sich um das Laden der CDI-Umgebung und injiziert die Beans direkt in den Testfall. Das Beste daran ist, dass bei der Verwendung des Embedded-CDI-Containers die Tests genauso schnell ablaufen wie bei einem einfachen Unittest. Wenn das alles ist, was Du benötigst, kannst Du den Guide jetzt zumachen und anfangen Tests zu erstellen.

Aber! Erzählt uns der Embedded Container die gesamte Geschichte? Wird die Komponente funktionieren, wenn diese in einem echten Container ausgeführt wird?

Einer der Vorteile von Arquillian ist, dass der gleiche Test in verschiedenen kompatiblen Containern ausgeführt werden kann, egal ob Embedded oder Remote. Wenn Du daran denkst, mehrere Container zu verwenden, lies einfach weiter.

Weitere Container hinzufügen

Wie Du weißt, wählt Arquillian den Container auf Grund des verfügbaren Adapters im Classpath. Um zu einem anderen Container zu wechseln, wechsele einfach den Container-Adapter vor dem Ausführen der Tests.

Zu einer bestimmten Zeit darf immer nur ein Container-Adapter im Classpath sein!

Ein Weg die Bibliotheken (Adapter) im Classpath zu wechseln ist es, die Abhängigkeiten in der pom.xml jedes Mal manuell zu ändern. Aber es gibt einen viel besseren Weg.

Wir können die sogenannten Maven-Profile verwenden, um die Abhängigkeiten in Gruppen zu verteilen: Eine Gruppe/Profil für jeden Container-Adapter und die zugehörigen, weiteren Abhängigkeiten. Zur Testdurchführung muss dann lediglich die richtige Gruppe bzw. das richtige Profil per Kommandozeilenparameter (-P) oder per Voreinstellung in der IDE gewechselt werden.

Öffne die pom.xml-Datei und erstelle ein neues Maven-Profil für den Weld-EE-Embedded-Container durch das Einfügen der folgenden Zeilen direkt unterhalb des schließenden <dependencies>-Element.

pom.xml
<!-- clip -->
<profiles>
    <profile>
        <id>arquillian-weld-ee-embedded</id>
        <dependencies>
            <dependency>
                <groupId>org.jboss.spec</groupId>
                <artifactId>jboss-javaee-7.0</artifactId>
                <version>1.0.3.Final</version>
                <type>pom</type>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.arquillian.container</groupId>
                <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
                <version>1.0.0.CR9</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.weld</groupId>
                <artifactId>weld-core</artifactId>
                <version>2.3.5.Final</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.6.4</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </profile>
</profiles>
<!-- clip -->

Als nächstes entfernst Du die jboss-javaee-7.0- und die Weld-EE Container-Adapter-Abhängigkeiten aus der Hauptsektion der <dependencies>. Das sollte dann so aussehen, wenn Du fertig bist:

pom.xml
<!-- clip -->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.jboss.arquillian.junit</groupId>
        <artifactId>arquillian-junit-container</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
<profiles>
    <profile>
        <id>arquillian-weld-ee-embedded</id>
        <dependencies>
            <dependency>
                <groupId>org.jboss.spec</groupId>
                <artifactId>jboss-javaee-7.0</artifactId>
                <version>1.0.3.Final</version>
                <type>pom</type>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.arquillian.container</groupId>
                <artifactId>arquillian-weld-ee-embedded-1.1</artifactId>
                <version>1.0.0.CR9</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.weld</groupId>
                <artifactId>weld-core</artifactId>
                <version>2.3.5.Final</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-simple</artifactId>
                <version>1.6.4</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </profile>
</profiles>
<!-- clip -->

Die Java-EE-API-Abhängigkeit ist ebenfalls in das Container-Adapter-Profil gewandert, da manche Container wie beispielsweise der Embedded GlassFish diese bereits mitbringen. Beide im Classpath zu haben würde zu Problemen führen. Darum dieser Tanz mit dem Classpath.

Jetzt fügen wir zwei weitere Profile in die pom.xml-Datei innerhalb des <profiles>-Elements ein. Zuerst für den Embedded GlassFish:

pom.xml
<!-- clip -->
<profile>
    <id>arquillian-glassfish-embedded</id>
    <dependencies>
        <dependency>
            <groupId>org.jboss.arquillian.container</groupId>
            <artifactId>arquillian-glassfish-embedded-3.1</artifactId>
            <version>1.0.0.CR4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.main.extras</groupId>
            <artifactId>glassfish-embedded-all</artifactId>
            <version>3.1.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</profile>
<!-- clip -->

und das andere Profil für den JBoss AS managed:

pom.xml
<!-- clip -->
<profile>
    <id>arquillian-jbossas-managed</id>
    <dependencies>
        <dependency>
            <groupId>org.jboss.spec</groupId>
            <artifactId>jboss-javaee-7.0</artifactId>
            <version>1.0.3.Final</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.as</groupId>
            <artifactId>jboss-as-arquillian-container-managed</artifactId>
            <version>7.1.1.Final</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</profile>
<!-- clip -->

Jetzt haben wir die Wahlmöglichkeit die Tests in einem der drei Container auszuführen.

Wenn du Probleme mit der pom.xml Datei hast, kannst Du die bisher erstelle direkt als gist datei herunterladen.

Test mit mehreren Containern

Aktualisierst Du das Projekt jetzt in Eclipse wirst Du feststellen, dass es nicht mehr gebaut wird. Das liegt daran, dass Du der IDE erst noch sagen musst, welches Maven-Profil sie verwenden soll. Also aktivieren wir mal das Weld-EE-embedded-Profil, um den Ausgangszustand wiederherzustellen.

In Eclipse gibt es zwei Wege, um Maven-Profile zu aktivieren:

  1. Die manuelle Konfiguration (Standardansatz)
  2. oder der Maven Profile Selector (durch die JBoss Tools bereitgestellt)

Aktives Maven-Profil setzen: Manuelle Konfiguration

Um das aktive Maven-Profil zu setzen, folge diesen Schritten:

  1. Rechts-Klick auf das Projekt und Auswahl von “Eigenschaften”
  2. Wähle den Maven-Eigenschaften-Reiter
  3. Gib die Profil-ID in das Aktive-Maven-Profiles-Feld ein (beispielsweise arquillian-weld-ee-embedded)
  4. Klicke auf den OK-Knopf und akzeptiere die Projektänderungen

Hier ist ein Screenshot des Maven-Eigenschaften-Dialogs, welcher die aktivierten Profile anzeigt:

Aktives Maven Profil setzen: Maven Profile Selector

Wenn Du die JBoss Tools installiert hast, wird das Auswählen des aktiven Maven-Profils noch einfacher:

  1. Rechts-Klick auf das Projekt und Auswahl von “Maven > Select Active Profiles…” (Alternativ kann dies auch per Ctrl+Shift+P oder dem Knopf aus der Toolbar erfolgen)
  2. Wähle die Box neben dem Profil, welches Du aktivieren willst, an (beispielsweise arquillian-weld-ee-embedded).
  3. Klicke auf den OK-Knopf.

Hier ist ein Screenshot des Maven-Profil-Selector-Dialogs, welcher die aktivierten Profile anzeigt:

Ist das Profil einmal aktiviert, sollte der Test wieder erfolgreich ausgeführt werden können.

Du weißt schon, dass der Test im Weld-EE-Embedded-Container funktioniert. Nun wechseln wir auf den GlassFish Embedded, indem die o.g. Schritte durchgeführt werden. Diesmal aktiviere allerdings das arquillian-glassfish-embedded-Profil. Führe den Test erneut aus. Du solltest auf der Konsole sehen, wie der GlassFish startet, gefolgt von einer erneuten green bar!

Du hast die Tests nun auf verschiedenen Embedded-Containern ausgeführt, einem CDI-Container (Weld) und einem Java-EE-Container (GlassFish). Bei beiden handelte es sich um Container, welche in der gleichen JVM wie die Tests laufen. Um wirklich sicher zu sein, müssen die Tests in einem alleinstehenden Container erfolgreich durchlaufen werden. Daher wechseln wir zum JBoss AS.

Um die Tests in einer alleinstehenden Instanz vom JBoss AS laufen zu lassen, muss diese erst einmal konfiguriert werden. Dies geschieht entweder:

  1. durch Herunterladen und Entpacken der Distribution an eine Stelle im Dateisystem außerhalb des Projekts oder
  2. du lässt Maven beides beim Bauen des Projekts selber machen.

Folge diesen Schritten, um JBoss AS 7 außerhalb des Projekts aufzusetzen:

  1. Lade JBoss AS 7 herunter
    (Versichere Dich, dass die Version, die Du auswählst, genau zu der Version passt, die Du in Deinem pom.xml für <artifactId>jboss-as-arquillian-container-managed</artifactId> angegeben hast!)
  2. Packe das Archiv aus
  3. (optional) Setze die JBOSS_HOME-Umgebungs-Variable auf den Pfad des ausgepackten Archivs.

Um Maven die Arbeit beim Bauen machen zu lassen, musst Du das folgenden XML-Fragment unter das schließende <id> Element des arquillian-jbossas-managed-Profils einfügen:

pom.xml
<!-- clip -->
<build>
    <plugins>
        <plugin>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>unpack</id>
                    <phase>process-test-classes</phase>
                    <goals>
                        <goal>unpack</goal>
                    </goals>
                    <configuration>
                        <artifactItems>
                            <artifactItem>
                                <groupId>org.jboss.as</groupId>
                                <artifactId>jboss-as-dist</artifactId>
                                <version>7.1.1.Final</version>
                                <type>zip</type>
                                <overWrite>false</overWrite>
                                <outputDirectory>target</outputDirectory>
                            </artifactItem>
                        </artifactItems>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
<!-- clip -->

Die Managed-JBoss-AS-7-Instanz benötigt ein wenig Arquillian-Konfiguration. Erstelle eine Datei mit dem Namen arquillian.xml unter src/test/resources und ändere den Wert des jbossHome-Property auf das Verzeichnis, in das Du den JBoss AS 7 installiert hast. Wenn Du Maven die Arbeit beim Bauen machen lässt (maven dependency plugin), lautet der Wert target/jboss-as-7.1.1.Final.

src/test/resources/arquillian.xml
<arquillian xmlns="http://jboss.org/schema/arquillian"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://jboss.org/schema/arquillian
        http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
    <container qualifier="jbossas-managed" default="true">
        <configuration>
            <property name="jbossHome">target/jboss-as-7.1.1.Final</property>
        </configuration>
    </container>
</arquillian>

Wechsele jetzt das aktive Maven-Profil auf arquillian-jbossas-managed und lass die Tests ein letztes mal laufen. Du solltest in der Konsole sehen, wie der JBoss AS 7 startet und ….. dann eine erneute green bar erscheint!

Die Meldungen von System.out werden direkt ins Serverlog und nicht auf die Konsole geschrieben.

Das ist der gleiche Test, nur diesmal in einem alleinstehenden (nicht eingebetteten) Java-EE-Container. Arquillian packt das Testarchiv, deployt es auf den Container als Java-EE-Archiv und führt den Test remote durch, erfasst dann die Ergebnisse und stellt sie der Eclipse-JUnit-Ergebnis-View (oder dem Maven-Surefire-Plugin) zur Verfügung.

Share the Knowledge

Find this guide useful?

There’s a lot more about Arquillian to cover. If you’re ready to learn more, check out the other available guides.

Feedback

Find a bug in the guide? Something missing? You can fix it by forking this website, making the correction and sending a pull request. If you’re just plain stuck, feel free to ask a question in the user discussion forum.

Recent Changelog

  • Sep 21, 2017: Fix(scripts) timeout for waiting for timestamp available on pages is by Matous Jobanek

See full history »