Datenzentrierte Tests in der Praxis: Komponenten- und Integrationstests für Talend-Jobs mit JUnit (Teil 3)

Im letzten Blog-Eintrag haben wir  gezeigt, wie DbUnit und WireMock verwendet werden können, um externe Abhängigkeiten für Komponententests von Talend-Jobs zu mocken. Vorrausetzung dabei war ein herkömmlicher Talend-Job, der als Nexus Artefakt vorliegt. Die ESB Variante von Talend erlaubt es aber nun auch, Services, z.B. REST-Services, mit Talend zu erstellen. Diese werden dann als OSGI-Bundle in einer Talend-Runtime deployed. In einer klassischen Java EE Anwendung können Services per Arquillian getestet werden. Arquillian ermöglicht es, JARs und WARs zum Testen zu generieren, auf einen Applikationserver zu deployen und dort Tests auszuführen. Glücklicherweise basiert die Talend-Runtime auf einem Apache Karaf Server und wird somit direkt von Arquillian unterstützt.

Datenzentrierte Tests von Services mithilfe von Arquillian – Ein praktisches Beispiel

Im ersten Schritt nutzen wir als Beispiel-Job den REST-Service, der von der Talend-Dokumentation verwendet wird:

https://help.talend.com/reader/akzLCJX2jurKtvXycoNGyQ/t0Dmfkg4yVYRj19mBr5n1Q

Dieser Rest Service liefert eine Liste von Mitarbeitern in einfacher Form zurück. Dem Job wird ein Context-Parameter hinzugefügt, der mit zurückgegeben wird, um eine erfolgreiche Manipulation des Contexts zu zeigen.Eine Gradle-Konfiguration könnte beispielsweise folgendermaßen aussehen:

apply plugin: 'java'

repositories {
    mavenCentral()
    maven {url "http://repository.jboss.org/nexus/content/groups/public"}
    <local nexus>
}

dependencies {

    ...
    testImplementation  'org.jboss.arquillian.container:arquillian-container-karaf-remote:2.2.1.Final'
    testImplementation  'org.jboss.arquillian.osgi:arquillian-osgi-bundle:2.2.2.Final'
    testImplementation  'org.jboss.logging:jboss-logging:3.3.2.Final'
    testImplementation  'org.jboss.arquillian.junit:arquillian-junit-container:1.4.1.Final'
    testImplementation  'org.jboss.resteasy:resteasy-client:3.6.3.Final'
    testImplementation  'org.jboss.resteasy:resteasy-jackson-provider:3.6.3.Final'
    

}

Ein Entpacken ist in diesem Fall nicht mehr nötig, da das OSGI-Bundle direkt als JAR vorliegt. Allerdings muss das Bundle in ein lokales Verzeichnis kopiert werden, damit es später von Arquillian deployed werden kann. Dies kann mit folgendem Gradle Task erledigt werden:

configurations {
    testJob
}

dependencies {

    ...   
    testJob 'com.example:rest-test:0.1.0-SNAPSHOT'

}

task copyJob(type: Copy) {
    from configurations.testJob.find { it.name.startsWith("rest-test-") }
    into "$buildDir/job"
}

afterEvaluate {
    test.dependsOn copyJob
}

Arquillian benötigt noch eine Konfiguration, die den Zugriff auf den Karaf-Server definiert:

<?xml version="1.0" encoding="UTF-8"?>
<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="karaf" default="true">
        <configuration>
            <property name="autostartBundle">true</property>
            <property name="jmxServiceURL">service:jmx:rmi://localhost:44444/jndi/rmi://localhost:1099/karaf-trun</property>
            <property name="jmxUsername">tadmin</property>
            <property name="jmxPassword">tadmin</property>
        </configuration>
    </container>
</arquillian>

Diese Variante der Konfiguration geht davon aus, dass es eine laufende Talend-Runtime gibt. Arquillian kann auch selber das Starten und Stoppen der Talend-Runtime übernehmen. Die Option autostartBundle definiert, dass die deployten Services direkt gestartet werden sollen.

Vorbereitungen abschlossen – Nun kann der Textdurchlauf in Arquillian durchgeführt werden

Die JUnit-Testklasse schaut nun folgendermaßen aus:

@RunWith(Arquillian.class)
public class KarafTest {
  ...
}

Zu beachten ist, dass hier ein spezieller Arquillian-Testrunner verwendet wird. Die Klasse enthält eine Methode, die das OSGI-Bundle bereitstellt:

@Deployment(testable = false)
public static JavaArchive createdeployment() {
    JavaArchive archive = ShrinkWrap.create(ZipImporter.class, "rest-test-0.1.0-SNAPSHOT.jar")
            .importFrom(new File("build/job/rest-test-0.1.0-SNAPSHOT.jar"))
            .as(JavaArchive.class);
    return archive;
}

Üblicherweise wird das JAR direkt aus dem Code erstellt. Da hier aber bereits ein fertiger Talend-Job zum Testen vorliegt, kann direkt das existierende JAR verwendet werden. Der Parameter testable=false gibt an, dass Arquillian kein JAR erstellt, welches Tests enthält, die auf dem Server ausgeführt werden. Eine Testmethode könnte nun folgendermaßen ausschauen:

@Test
@RunAsClient
public void testCallRest() throws Exception {

    Client client = ClientBuilder.newBuilder().build();
    WebTarget target = client.target("http://localhost:8088/services/employees");
    Response response = target.request().get();
    Employees value = response.readEntity(Employees.class);
    response.close();

    assertEquals(value.getEmployees().getEmployee().get(0).getContext(), "Arquillian");

}

Wichtiger Hinweis : Testdurchführung wird nur auf Client-Seite unterstützt

Mit der Annotation @RunAsClient wird definiert, dass der Test nicht auf Server-Seite, sondern auf Client-Seite durchgeführt werden soll. Um einen Test auf dem Server auszuführen, müsste dieser Teil des deployten JARs sein. Da hier das JAR aber der Talend-Job ist, muss der Test auf Client-Seite ausgeführt werden. Dies ist letztlich auch gut so, da ja der Aufruf des Talend-REST-Services getestet werden soll. Ein RestEasy Client hilft hier im Beispiel beim Aufruf des Services und beim Desrealisieren der Antwort. Zu beachten ist auch, dass in der Testmethode selber gar kein Start eines Talend-Jobs erfolgt. Dies passiert allein durch das automatisierte Deployment des OSGI-Bundles durch Arquillian.

Modifizierung des Contexts mithilfe einer Konfigurationsdatei oder JMX-Befehlen

Ein offener Punkt ist noch das Anpassen des Contexts, um die Abhängigkeiten des Talend-Jobs zu mocken. Zwei Möglichkeiten werden hier beschrieben:

https://community.talend.com/t5/Design-and-Development/how-to-change-context-values-of-a-deployed-job-as-osgi/td-p/67026

Der Context kann entweder über eine Konfigurationsdatei angepasst werden oder über JMX-Befehle. Für dieses Beispiel wurde einfach eine für den Test vordefinierte Konfigurationsdatei direkt per Gradle in die Talend-Runtime kopiert:

ext {
    talendRuntime = ".../Runtime_ESBSE/container/etc/"
}


task copyConfig(type: Copy) {
    dependsOn processTestResources
    from "$buildDir/resources/test/TestRest.cfg"
    into talendRuntime
}

afterEvaluate {
    test.dependsOn copyConfig
}

Die Frameworks DbUnit und WireMock, die im letzten Eintrag dieser Blog-Reihe vorgestellt wurden, können auch hier wieder zum Testen von Talend-REST-Services verwendet werden. Die Arquillian Persistence Extension, die auf DbUnit basiert und etwas komfortabler zu verwenden ist, kann leider nicht verwendet werden, da der Test auf Client-Seite ausgeführt wird.

Zusammenfassung: Datenzentrierte Tests in Talend sind in verschiedener Ausführung möglich

In dieser Blogreihe haben wir einen Überblick über verschiedene Möglichkeiten gegeben, wie datenzentrierte Tests in Talend durchgeführt werden können. Im Kundenprojekt bieten Datentests eine effektive Möglichkeit, die Einsatzfähigkeit der entwickelten Lösung zu prüfen. Die ausgeführten Tests sind auch wichtiger Bestandteil von abschließenden Präsentationen vor dem Kunden. Somit ist es im Projektgseschäft essentiell, sich mit Datentests auseinanderzusetzen und deren Umsetzbarkeit in verschiedenen Softwarelösungen zu kennen. In unserer Blogreihe haben wir bespielhaft erläutert, wie Datentests in Talend durchgeführt werden können.

Teil 1 beschreibt, wie sich ein Talend Job leicht in einer klassischen JUnit Testumgebung testen lässt. Diese Art von Tests kann im Anschluss leicht in eine Jenkins Build-Pipeline integriert werden, um kontinuierlich die Software-Qualität zu überwachen. Im anschließenden Teil 2 dieser Blogreihe wurden die Vorteile dargestellt, die sich für Datentests durch die Einbindung von DbUnit und WireMock aus dem Java-Umfeld ergeben. Im letzten Teil 3 wurde schließlich die Möglichkeit zum Testen von Services in Talend anhand von sogenannten REST-Services veranschaulicht.

Unser Fazit dieser Blog-Reihe: JUnit und die damit verbundenen Frameworks können für Komponententests problemlos für das automatisierte Testen von Talend-Jobs verwendet werden. Abhängigkeiten, wie Services und Datenbanken, können leicht gemocked werden. Somit kann der Anwender den Scope des Tests genau definieren. Zum Testen eigenen sich sowohl einzelne Komponenten als auch das Zusammenspiel von mehreren Komponenten.  Auch das Zusammenspiel von Talend-Jobs und klassischen Java Backend-Services lässt sich so leicht automatisiert testen.

Weitere Informationnen zu Talend finden Sie auf unserer Technologieseite.

Bei Rückfragen zu diesem Artikel oder Bedarf an Unterstützung können Sie sich gerne an unseren Ansprechpartner für den Technologiebereich Talend wenden: Dr. Michael Daum, michael.daum@prodato.de

Prodato verbindet.

Autor

Dr. Philipp Baumgärtel
Lead Consultant

philipp.baumgaertel@prodato.de