//

Testen von Mule-ESB-Applikationen (Teil 3/3): System-End-To-End-Tests mit Docker

14.6.2015 | 8 Minuten Lesezeit

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 letzte in einer Reihe von Artikeln zum Thema Testen von Mule-ESB-Projekten auf allen Ebenen (Teil 1 , Teil 2 ). Der Fokus in diesem Artikel liegt auf auf einem übergreifenden System-End-to-End-Test in einem Mule-Projekt, welches sich durch das Aufsetzen der Infrastruktur mit dem ESB und einem Mock Server mit der Hilfe von Docker auszeichnet.

Infrastruktur

Um einen System-End-to-End-Test für eine Mule-Applikation durchzuführen, benötigen wir drei Systemkomponenten:

  • App: Zuerst brauchen wir die zu testende Mule-Applikation.
  • Tester: Der Tester ist für das Testen der Anwendung verantwortlich. Das Testen kann durch einfache Tests, die die API-Aufrufe durchführen und das Ergebnis verifizieren, oder durch Test-Tools, welche komplexe orchestrierte Aufrufe durchführen, wie z. B. JMeter , durchgeführt werden.
  • Mock: Zusätzlich brauchen wir einen oder mehrere System-Mocks, die Systeme darstellen, von denen die Anwendung abhängig ist. Mountebank kann solch eine Funktionalität bereitstellen.

Solch ein System-End-to-End-Test-Setup würde wie folgt aussehen:

Docker

Docker ist eine Open-Source-Technologie, die Virtualisierung von Maschinen in isolierten Containern auf einem Betriebssystem ermöglicht. Durch die Verwendung von Linux-Technologien wie Cgroups und Namespaces erlaubt es das schnelle und ressourceneffiziente Erzeugen von Containermaschinen, womit man eine portable, nachvollziehbare und konsistente Infrastruktur aufbauen kann. Dies ist vor allem für die Erstellung, Durchführung und Nachvollziehbarkeit von Testszenarien, die Infrastruktur-basiert sind, ein großer Vorteil.

Um eine bessere Integration solch eines System-End-to-End-Tests in eine Continuous Integration Pipeline sicherzustellen, ist die Verwendung von Container-Technologie von Vorteil. Die Verwendung von Docker zum Beispiel erlaubt das beschleunigte Starten einer isolierten Mule-Instanz mit der zu testenden Anwendung und dem Mock Server.

Zu testende Anwendung

Wir nehmen das folgende einfache Szenario als Beispiel. Eine Mule-Applikation stellt eine REST-API auf Port 8080 zur Verfügung und ruft intern einen REST-Backend-Service auf Port 9000 auf. Solch eine Applikation könnte wie folgt aussehen:

In diesem Beispiel sehen wir einen HTTP-Endpoint, der auf Port 8080 lauscht und der alle Anfragen an den REST-API-Router weiterleitet. Die Anfrage an /myResource wird im unteren Sub Flow landen und einen auswärtigen HTTP-Aufruf zum Server auf Port 9000 auslösen. Das Ergebnis wird in einen String transformiert und an den Aufrufer zurückgegeben. Im Falle von Exceptions greift eine Exception-Strategie und wird ein entsprechend passendes Ergebnis zurückliefern.

Wir nehmen an, wir haben unsere Mule-Anwendung bereits als einzelne Applikation in einem Docker-Container vorliegen, wie in diesem Blog-Artikel beschreiben.

Mock Server

Um der Mule-Applikation Aufrufe zu einem potenziellen Backend-Service in einem System-End-to-End-Szenario zu ermöglichen, kann eine Technologie wie Mountebank verwendet werden.

Mountebank ist ein Open-Source-Tool, das plattformunabhängige Multi-Protokoll-Testdoubletten auf der Netzwerkschicht bereitstellt. Eine zu testende Applikation muss nur auf die IP oder URL der Mountebank-Instanz verweisen statt auf die reale Abhängigkeit. Das ermöglicht, die Applikation durch alle Applikationsschichten zu testen, wie man es sonst traditionell mit Stubs und Mocks tun würde. Unterstützte Protokolle sind HTTP, HTTPS, TCP und SMTP.

Für unser Szenario würde der Mountebank Imposter wie folgt definiert werden, um eine gemockte Antwort auf Port 9000 zurückzugeben:

1{
2  "port": 9000,
3  "protocol": "http",
4  "name": "My Mock",
5  "mode": "text",
6  "stubs": [
7    {
8      "responses": [
9        {
10          "is":
11          {
12            "statusCode": 200,
13            "headers": {
14              "Content-Type": "application/json"
15            },
16            "body": "{ \"message\": \"You got mocked data\" }"
17          }
18        }
19      ],
20      "predicates": [
21        {
22          "equals": {
23            "path": "/anotherResource"
24          }
25        }
26      ]
27    }
28  ]
29}

Wir nehmen an, dass der Mock Server ebenfalls in einem Docker-Container aufgesetzt wurde, wie in diesem Blog-Artikel beschrieben.

Test-Definition

Nun zu unserem Test. Wir benutzen eine einfache JUnit-Integration unter Verwendung der rest-assured Bibliothek, integriert in einem Maven-Build. Der Test ruft die REST-API auf und verifiziert, dass die Antwort die gemockten Daten des Mock Servers enthält. An diesem Punkt könnte man auch direkt über die Mountebank-REST-API die Anfragen an den Mock Server verifizieren.

Solch ein Test könnte wie folgt aussehen:

1public class SystemIT {
2 
3  @Test
4  public void testMyResource() {
5 
6    RestAssured.baseURI = System.getProperty("system.url");
7    RestAssured.defaultParser = Parser.JSON;
8 
9    // Verify an system end-to-end call
10    given()
11            .param("mimeType", "application/json")
12            .get("/api/myResource")
13            .then().assertThat()
14            .header("content-type", containsString("application/json"))
15            .body("message", equalTo("You got mocked data"));
16  }
17}

Test Konfiguration

Die Automatiserung dieses Szenarios wird mithilfe von Maven und des docker-maven-plugin erreicht. Zu diesem Zweck werden zwei Docker Images definiert, eines für die Mule-Anwendung und eines für den Mock Server:

1<plugin>
2  <groupId>org.jolokia</groupId>
3  <artifactId>docker-maven-plugin</artifactId>
4  <version>0.11.5</version>
5 
6  <configuration>
7    <dockerHost>${boot2docker.url}</dockerHost>
8 
9    <images>
10      <!-- Mule app container configuration -->
11      <image>
12        <name>mule-app</name>
13        <alias>mule-app</alias>
14        <run>
15          <ports>
16            <port>${webservice.port}:${webservice.port}</port>
17          </ports>
18          <links>
19            <link>rest-mock:backend</link>
20          </links>
21          <wait>
22            <!-- The plugin waits until this URL is reachable via HTTP ... -->
23            <log>Server startup</log>
24            <url>${boot2docker.address}:${webservice.port}/api/console</url>
25            <time>8000</time>
26            <shutdown>500</shutdown>
27          </wait>
28          <log>
29            <prefix>Mule</prefix>
30            <date>ISO8601</date>
31            <color>blue</color>
32          </log>
33        </run>
34        <build>
35          <from>cpoepke/muledocker:latest</from>
36          <tags>
37            <tag>mule-app</tag>
38          </tags>
39          <command>/opt/mule-standalone-3.6.1/bin/mule -M-Dbackend.host=$BACKEND_PORT_9000_TCP_ADDR 
40-M-Dbackend.port=$BACKEND_PORT_9000_TCP_PORT</command>
41          <assembly>
42            <mode>dir</mode>
43            <basedir>/</basedir>
44            <descriptor>assembly-app.xml</descriptor>
45          </assembly>
46        </build>
47      </image>
48      <!-- Backend mock container configuration -->
49      <image>
50        <name>rest-mock</name>
51        <alias>rest-mock</alias>
52        <run>
53          <ports>
54            <port>2525:2525</port>
55            <port>9000:9000</port>
56          </ports>
57          <log>
58            <prefix>Mock</prefix>
59            <date>ISO8601</date>
60            <color>yellow</color>
61          </log>
62          <wait>
63            <!-- The plugin waits until this URL is reachable via HTTP ... -->
64            <log>Server startup</log>
65            <url>${boot2docker.address}:2525</url>
66            <time>2000</time>
67            <shutdown>500</shutdown>
68          </wait>
69        </run>
70        <build>
71          <from>cpoepke/mountebank-basis:latest</from>
72          <tags>
73            <tag>rest-mock</tag>
74          </tags>
75          <command>mb --configfile /mb/imposters.ejs --allowInjection</command>
76          <assembly>
77            <mode>dir</mode>
78            <basedir>/</basedir>
79            <descriptor>assembly-mock.xml</descriptor>
80          </assembly>
81        </build>
82      </image>
83    </images>
84  </configuration>

In diesem Beispiel sind das Port Mapping und die Docker-Links zwischen den Containern erkennbar.

Um die Container für einen Test zu starten und zu stoppen, muss die folgende Integration-Test-Konfiguration aufgesetzt werden, um die Maven-Phasen zu konfigurieren:

1<!-- Connect start/stop to pre- and
2       post-integration-test phase, respectively if you want to start
3       your docker containers during integration tests -->
4  <executions>
5    <execution>
6      <id>start</id>
7      <phase>pre-integration-test</phase>
8      <goals>
9        <!-- "build" should be used to create the images with the
10             artefacts -->
11        <goal>build</goal>
12        <goal>start</goal>
13      </goals>
14    </execution>
15    <execution>
16      <id>stop</id>
17      <phase>post-integration-test</phase>
18      <goals>
19        <goal>stop</goal>
20      </goals>
21    </execution>
22  </executions>
23</plugin>

Dies startet die Docker-Container mit docker:start vor der Maven-pre-integration-test-Phase und stoppt diese mit docker:stop in der Maven-post-integration-test-Phase.

Um diese Integrationstests auszuführen, benötigen wir das failsafe-Plugin, welches unsere System-End-to-End Tests in der Maven-integration-test-Phase inklusive Environment-Variablen ausführt.

1<!-- fails-safe-plugin should be used instead of surefire so that the container gets stopped even
2     when the tests fail -->
3<plugin>
4  <groupId>org.apache.maven.plugins</groupId>
5  <artifactId>maven-failsafe-plugin</artifactId>
6  <version>2.18.1</version>
7  <executions>
8    <execution>
9      <id>integration-test</id>
10      <phase>integration-test</phase>
11      <goals>
12        <goal>integration-test</goal>
13      </goals>
14    </execution>
15    <execution>
16      <id>verify</id>
17      <phase>verify</phase>
18      <goals>
19        <goal>verify</goal>
20      </goals>
21    </execution>
22  </executions>
23  <configuration>
24    <systemPropertyVariables>
25      <!-- Needs to be repeated here (the following two lines strangely doesn't work when the next line is omitted although)
26           Maven, you little sneaky beast ... -->
27      <!--<system.port>${webservice.port}</system.port>-->
28 
29      <!-- Map maven variables to system properties which in turn can be used in the test classes -->
30      <system.url>http://${boot2docker.ip}:${webservice.port}</system.url>
31    </systemPropertyVariables>
32  </configuration>
33</plugin>
34 
35<!-- Tell surefire to skip test, we are using the failsafe plugin -->
36<plugin>
37  <groupId>org.apache.maven.plugins</groupId>
38  <artifactId>maven-surefire-plugin</artifactId>
39  <version>2.18.1</version>
40  <configuration>
41    <skip>true</skip>
42  </configuration>
43</plugin>

Anmerkung: Bitte vergessen Sie nicht, das VM Port Forwarding auf Mac und Windows für boot2docker!

Test-Ausführung

Die Ausführung der Tests und ihre Integration in eine Continuous Integration oder Delivery Pipeline kann durch das „mvn verify“-Kommando durchgeführt werden. In dem Log ist zu sehen, dass alle Container starten, die Ausführung wartet, bis der Start durchgeführt wurde, die System-End-to-End-Tests durchgeführt werden und wie die Container gestoppt werden:

1cpoepke:sys-test cpoepke$ mvn verify
2[INFO] Scanning for projects...
3[INFO]                                                                         
4[INFO] ------------------------------------------------------------------------
5[INFO] Building System Test - Mule End to End Test Demo 1.0.0-SNAPSHOT
6[INFO] ------------------------------------------------------------------------
7...
8[INFO] --- docker-maven-plugin:0.11.5:build (start) @ sys-test ---
9[INFO] Reading assembly descriptor: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/src/main/docker/assembly-app.xml
10[INFO] Copying files to /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/docker/mule-app/build/maven
11[INFO] Building tar: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/docker/mule-app/tmp/docker-build.tar
12[INFO] DOCKER> Created image [mule-app] "mule-app"
13[INFO] DOCKER> Tagging image [mule-app] "mule-app": mule-app
14[INFO] Reading assembly descriptor: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/src/main/docker/assembly-mock.xml
15[INFO] Copying files to /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/docker/rest-mock/build/maven
16[INFO] Building tar: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/docker/rest-mock/tmp/docker-build.tar
17[INFO] DOCKER> Created image [rest-mock] "rest-mock"
18[INFO] DOCKER> Tagging image [rest-mock] "rest-mock": rest-mock
19[INFO] 
20[INFO] --- docker-maven-plugin:0.11.5:start (start) @ sys-test ---
21[INFO] DOCKER> Starting container 4ee608ab49b9
22[INFO] DOCKER> Creating and starting container 4ee608ab49b9 [rest-mock] "rest-mock"
232015-06-09T22:49:36.349+02:00 Mock> mountebank v1.2.122 now taking orders - point your browser to http://localhost:2525 for help
24[INFO] DOCKER> Waited on url https://192.168.59.103:2525 and on log out 'Server startup' 2091 ms
25[INFO] DOCKER> Starting container b7069c9653cd
26[INFO] DOCKER> Creating and starting container b7069c9653cd [mule-app] "mule-app"
272015-06-09T22:49:38.634+02:00 Mule> MULE_HOME is set to /opt/mule-standalone-3.6.1
282015-06-09T22:49:38.642+02:00 Mule> Running in console (foreground) mode by default, use Ctrl-C to exit...
292015-06-09T22:49:38.649+02:00 Mule> MULE_HOME is set to /opt/mule-standalone-3.6.1
302015-06-09T22:49:39.845+02:00 Mule> Running Mule...
31...
32[INFO] DOCKER> Waited on url https://192.168.59.103:8080/api/console and on log out 'Server startup' 8114 ms
33[INFO] 
34[INFO] --- maven-failsafe-plugin:2.18.1:integration-test (integration-test) @ sys-test ---
35[INFO] Failsafe report directory: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/failsafe-reports
36 
37-------------------------------------------------------
38 T E S T S
39-------------------------------------------------------
40Running de.cpoepke.mule.demo.SystemIT
41Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.871 sec - in de.cpoepke.mule.demo.SystemIT
42 
43Results :
44 
45Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
46 
47[INFO] 
48[INFO] --- docker-maven-plugin:0.11.5:stop (stop) @ sys-test ---
49[INFO] DOCKER> Stopped and removed container b7069c9653cd [mule-app] "mule-app"
50[INFO] DOCKER> Stopped and removed container 4ee608ab49b9 [rest-mock] "rest-mock"
51[INFO] 
52[INFO] --- maven-failsafe-plugin:2.18.1:verify (verify) @ sys-test ---
53[INFO] Failsafe report directory: /Volumes/Projects/Current/Mule-ESB/mule-end-to-end-test-demo/sys-test/target/failsafe-reports
54[INFO] ------------------------------------------------------------------------
55[INFO] BUILD SUCCESS
56[INFO] ------------------------------------------------------------------------
57[INFO] Total time: 21.396 s
58[INFO] Finished at: 2015-06-09T22:49:50+02:00
59[INFO] Final Memory: 22M/206M
60[INFO] ------------------------------------------------------------------------

Fazit

Allumfassendes Testen wird gemeinhin als essentieller Bestandteil eines guten Software-Entwicklungsprozesses verstanden. Diese Tests automatisiert und auf allen Ebenen der Testpyramide durchzuführen ist erstrebenswert. Folglich ist auch das End-to-End-Testen einer Mule-Anwendung von Bedeutung.

Wir haben in diesem Artikel gezeigt, wie man eine voll automatisierte System-End-to-End-Test-Infrastruktur aufbauen kann. Wir haben dies am Beispiel vom Testen von Mule-Anwendungen mit Docker und Mountebank gemacht. Es ist aber auch möglich, dieses Test-Setup für andere Szenarien und Applikationstypen wiederzuverwenden, falls ein End-to-End-Test gewünscht ist.

Eine volles lauffähiges Beispiel dieses Szenarios befindet sich auf Github als Demo.

Serie

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

Beitrag teilen

Gefällt mir

0

//

Weitere Artikel in diesem Themenbereich

Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.

//

Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.