Testen von Mule ESB Applikationen (Teil 1/3): Modultests und funktionales Testen

1 Kommentar

Abstrakt

Im allgemeinen Konsens wird das Testen von Software als integraler Bestandteil des Software Entwicklungsprozesses gesehen. Tests sollten in allen Phasen der Softwareentwicklung eingesetzt werden: von Unit- bis zu Akzeptanztests. Vor allem im Software Engineering bilden zusammenhängende und automatisierte Tests ein Sicherheitsnetz gegen regressive und inkompatible Änderungen.

In Integrationsprojekten mit Mule ESB sind diese Aspekte auch von Belang. Komponenten in Mule-Flows, die Flows selber und deren Integration müssen intensiv getestet werden.

Dieser Artikel ist der erste Teil einer Serie zum Thema des Testens von Mule ESB Projekten auf allen Ebenen. Der Fokus dieses Artikels liegt auf Modul- und funktionalen Tests, die die kleinsten Komponenten in einem Mule Projekt testen.

Testen von Software – Die Test Pyramide

Bevor wir tiefer in das Thema eintauchen, werfen wir einen Blick auf den Kontext des Testens selber. Idealerweise wird in Software Projekten von unten aufwärts getestet. Zuerst mit einer großen Basis an Testfällen, die, idealerweise automatisch bei jedem Build, durch Unit- und Modultests die kleinsten Komponenten testen. Auf dem Weg durch die Architektur nimmt die Anzahl an Testfällen ab, da die nun größeren Komponenten Kompositionen aus den bereits getesteten Komponenten sind. Dies geschieht bis man die Spitze der Pyramide erreicht, wo die Applikation als Einheit durch manuelle Überwachung von Tests oder manuelles Testen geprüft wird. [1]

automatedtestingpyramid

Source: http://watirmelon.com/2012/01/31/introducing-the-software-testing-ice-cream-cone/
Automatisierte Test Pyramide

Modultests

Auf der untersten Ebene verifizieren Unit- und Modultests die korrekte Funktionalität von Klassen. Diese Klassen können bei Mule Projekten einfach Erweiterungen und Anpassungen des Mule Frameworks sein. Beispiele sind:

  • Individualisierte Transformer
  • Individualisierte Komponenten
  • Individualisierte Evaluationen von Ausdrücken
  • Und generell alle Spring Beans die eine Mule Applikation nutzen soll. Typischerweise sind solche Beans in einem Mule-Module-Projekt Teil einer Bibliothek und werden deswegen separat beim Bauen der Bibliothek getestet.

Unit Tests im klassischen Sinne testen die Funktionalität von einzelnen Klassen, ohne Mule ausführen zu müssen. Eine einfache Java Klasse (POJO) und ihr Testfall am Beispiel einer Kunden Transformationslogik könnten so aussehen:

public class CustomerTransformationComponent {
 
   public Map<String, Object> tranformCustomer(Customer customer) {
      Map<String, Object> returnMap = Maps.newHashMap();
      returnMap.put("name", customer.getName());
      // Fields mapping
      // ...
      return returnMap;
   }
}
 
public class CustomerTranformationComponentTest {
 
   @Test
   public testTransform() {
      Customer testCustomer = new Customer();
      // Create test data
      Map<String, Object> customerMap = new CustomerTransformationComponent()
            .tranformCustomer(testCustomer);
      // Assert test data
   }
}

Das Test Compability Kit (TCK)  des Mule Frameworks stellt für das Testen von Erweiterungen und Anpassungen einen Mule-Kontext bereit [3]. Zu jedem Mule-Komponententyp gibt es zu diesem Zweck eine abstrakte Elternklasse, welche von org.mule.tck.junit4.AbstractMuleTestCase abgeleitet ist. Diese sind in der Bibliothek mule-core-3.5.2-tests.jar für Mule in der Version 3.5.2 untergebracht.

Eine Java-Komponente, die das Mule-Interface Callable implementiert,  mit einer komplexeren Logik, die auf den Mule Kontext angewiesen ist, kann mit der entsprechenden Klasse SimpleJavaComponentTestCase getestet werden:

public class CustomerComponent implements Callable {
 
   @Autowired
   public CustomerService service;
 
   @Overwrite
   public Object onCall(MuleEventContext eventContext) throws Exception {
      String customerId = (String) eventContext.getMessage().getPayload();
 
      Customer customer = service.getCustomer(customerId);
 
      Map<String, Object> customerDetails = transformCustomer(customer);
 
      return customerDetails;
   }
}
 
public class CustomerComponentTest extends SimpleJavaComponentTestCase {
 
   @Test
   public testOnCall() {
      // Create test data
      MuleEvent event = getTestEvent(payload, muleContext);
      new CustomerComponent().onCall(new DefaultMuleEventContext(event));
      // Assert test data
   }
}

Folgende Vorteile bringen solche Unit Tests:

  • Komponenten, die mit den TCK Testfällen getestet werden, stellen sicher, dass das Verhalten der Komponente mit dem Mule Framework kompatibel ist.
  • Das Verwenden von TCK Testfällen erlaubt es Entwicklern, sich auf das Schreiben von Test für spezifisches Verhalten ihrer Komponenten zu konzentrieren.
  • Für den Fall, dass eine Methode in der Komponenten API nicht vom TCK Test Fall getestet wird, stellt der Testfall eine abstrakte Methode für den Test zu Verfügung. Dadurch kann der Entwickler Tests für alle Bereiche der Komponente schreiben.
  • Das TCK stellt ein Standard Testmodell zur Verfügung, welches aus einfachen Testklassen besteht. Der Entwickler muss für seine Testfälle nicht immer wieder neue Testklassen schreiben, sondern kann die Klassen aus dem TCK verwenden, um Standardfunktionalität zu testen. Z.B. wird der Mule Lebenszyklus der Komponente automatisch mit getestet.

Funktionales Testen von Mule Komponenten

Wenn es um das Testen der Interaktion und der Komposition zwischen Komponenten in Teil Flows oder „einfacheren“ Flows geht, werden funktionale Tests empfohlen [4]. Da Mule ESB leichtgewichtig und in Tests leicht integrierbar ist, wird die org.mule.tck.junit4.FunctionalTestCase Klasse des TCK empfohlen, um Teile von oder ganze Flows zu testen. Wenn man einen Unit-Test mit dieser Klasse erstellt, enthält diese automatisch eine integrierte Mule-Instanz mit einem Mule-Kontext, um funktionale Tests der Mule Flows durchzuführen.

Ein Schwerpunkt dieser Tests sind die folgenden Aspekte:

  • Funktionalität der Nachrichten Flows
  • Nachrichten Validation und Regel basiertes Routing in den Flows
  • Und deren Fehler Behandlung

Ein Beispiel Teil Flow welcher getestet werden sollte, könnte so aussehen

<sub-flow name="subFlow" doc:name="subFlow">	 	 
 <component class="de.codecentric.example.CustomerComponent" doc:name="Java"/>	 	 
</sub-flow>

Um diesen Teil Flow ausführen zu können, kann man den Aufruf des Flows mit einem VM Endpoint durchführen, der in einer Test Ressourcen XML Datei definiert wird:

<flow name="TestFlow" doc:name="TestFlow">	 	 
 <vm:inbound-endpoint exchange-pattern="request-response" path="TestFlow" doc:name="VM endpoint"/>	 	 
 <flow-ref name="subFlow" doc:name="Call sub flow for testing"/>	 	 
</flow>

Der passende Unit Test könnte wie folgt aussehen:

public class SubFlowTest extends FunctionalTestCase {
 
   @Test
   public void testFlow() throws Exception{
      MuleClient client = muleContext.getClient();
      String inputPayload = "550e8400-e29b-11d4-a716-446655440000";
      // Create test data
      MuleMessage reply = client.send("vm://TestFlow", inputPayload, null, 5000);
 
      assertNotNull(reply);
      assertNotNull(reply.getPayload());
      // Assert test data
   }
 
    @Override
    protected String[] getConfigFiles() {
        return new String[]{"./src/test/app/sub-flow-test.xml", 
            "./src/main/app/sub-flow.xml"};
    }
}

Das Überschreiben der getConfigFiles() Methode stellt dem Testfall die benötigten Mule- und Spring-Konfigurationsdateien zur Verfügung. Wir empfehlen dabei, die Produktions XML-Beschreibung und die Test-XML-Konfiguration in separaten XML-Dateien testspezifisch unter zu bringen.

Dieses einfache Beispiel kann ohne das Mocken oder das interne Anpassen von Flows getestet werden. Mule stellt eine Möglichkeit zur Verfügung, mittels <test:component/> Komponenten zu einem Flow hinzuzufügen, welche Mocking und Test-Funktionalität bereitstellen. Dies hat leider den Nachteil, dass die Flow-Beschreibung mit Testinformationen gemischt wird. Wir empfehlen für solche Fälle die Bibliothek MUnit, welche im nächsten Blog Artikel beschrieben wird.

Das Testen der (Teil-)Flows mit einer integrierten Mule-Instanz und mit einer sauberen Trennung der Flow-Beschreibung zwischen Test und Produktion birgt folgende Vorteile:

  • Die Konfiguration und die Flows können isoliert von einander getestet werden, welches eine saubere Separation der Tests und eine Reduktion der Größe der Testfälle bewirkt. Fehler können auf diese Weise gezielter identifiziert werden, da sie explizit in Testfällen lokalisiert werden können.
  • Es ist nicht gewünscht Mule-Standardkomponenten zu testen, da man davon ausgehen kann, das diese bereits ausführlich getestet worden sind. Deswegen sollten nur die Pfade und Komponenten eines Flows getestet werden, die vom Entwickler erstellt worden sind.
  • Testfälle benötigen ihre eigene Test-Infrastruktur, welche idealerweise aus In-Memory Infrastruktur-Komponenten besteht (z.B. VM als Transport, ActiveMQ für JMS oder H2 als Datenbank). Dies ist notwendig, da die Produktionsumgebung nicht immer automatisiert oder integriert in Unit-Tests aus Lizenz-, Ressourcen- oder Performanzgründen bereitgestellt werden kann.
  • Erhöhte Wiederverwendung zwischen Tests, z.B. von der In-Memory Infrastruktur, kann erreicht werden durch einmalige Bereitstellung der Konfiguration für alle Testfälle.

Fazit

Wir haben in diesem Blog Artikel eine Einführung in die ersten Schritte des Testens von Mule-Applikationen gegeben. Zunächst haben wir beschrieben, wie auf der niedrigsten Schicht Komponenten und (Teil-)Flows einer Mule-Applikation getestet werden können und welche Vorteile dies bringt. Wir haben zu diesem Zweck klassische Unit-Tests mit JUnit, mit Mule-Kontext mit dem TCK-Framework und funktionale Tests des TCK vorgestellt. Diese Tests können in Mule-Applikation, die nur aus einem Modul bestehen, oder in Bibliotheken, welche Komponenten und Teil-Flows für Multi-Modul Mule-Applikationen enthalten, angewendet werden.

Serie

Dieser Artikel ist Teil einer Mule ESB Serie zum Thema Testen von Mule Applikationen:

Referenzen

[1] http://martinfowler.com/bliki/TestPyramid.html
[2] http://watirmelon.com/2012/01/31/introducing-the-software-testing-ice-cream-cone/
[3] http://www.mulesoft.org/documentation/display/current/Unit+Testing
[4] http://www.mulesoft.org/documentation/display/current/Functional+Testing

Conrad ist Business Development Manager bei der codecentric AG. Er sieht sich selbst als „Coding-Software-Architekt“, Entwickler und in allen anderen Rollen, die für die erfolgreiche Durchführung eines Projekts vonnöten sind. Derzeit fokussiert er sich auf dem Aufbau der Partnerschaft mit Amazon Web Services und auf die in dem Rahmen entstehenden Projekte. Sein persönliches Ziel ist es, nach neuem Wissen in der IT-Industrie zu streben.

In seiner Freizeit macht er sich in Berlin für die IT Community stark, als Gründer und Hauptorganisator des Microservices Meetups Berlin, Co-Organizer des DDD Meetups, des Serverless Meetups und als Mitglied im Organisations-Komitee der MicroXchg Konferenz und KanDDDinsky der Berliner DDD Konferenz.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.