03.06.2017 Aufrufe

20170526-JavaSpektrum-Microservces-testautomation-1

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

Petri Heil!<br />

Testautomation einer<br />

Microservice-<br />

Architektur – Teil 1<br />

Rudolf Grötz, Dominik Kukacka, Nebojsa Nikolic<br />

<br />

IntegrationsSpektrum<br />

In Zeiten der Digitalisierung ist es für dynamische Unternehmen notwendig,<br />

technische Strukturen zu etablieren, die den immer schneller<br />

werdenden Marktanforderungen gerecht werden. Der digitale Darwinismus<br />

fordert Strukturen, die sowohl agil als auch schnell genug sind,<br />

diesen Anforderungen gerecht zu werden.<br />

Charles Darwin hat in seinen Werken einen wichtigen<br />

E Punkt erarbeitet: Es sind nicht die Stärksten, die überleben,<br />

und auch nicht die Intelligentesten. Es sind vielmehr jene,<br />

die sich am schnellsten den neuen Gegebenheiten anpassen<br />

können.<br />

Microservices als Antwort auf den digitalen<br />

Darwinismus<br />

Unternehmen wie Netflix, Amazon und Google haben schnell<br />

erkannt, die notwendigen technischen Strukturen anzupassen<br />

und zu implementieren. Geschafft wurde das durch den Einsatz<br />

von Microservices als neues Architekturprinzip. Weg vom<br />

Monolithen, hin zu kleinen, schlanken, unabhängigen Services,<br />

die über standardisierte APIs kommunizieren. Statt einer<br />

einzelnen monolithischen Anwendung, die die gesamte Geschäftslogik<br />

enthält, erledigt ein flexibles Netzwerk aus Microservices<br />

alle komplexen Vorgänge.<br />

In dieser 2-teiligen Artikelserie gehen wir auf die Entwicklung<br />

einer mobilen Anwendung ein. Diese basiert auf einer<br />

Microservice-Architektur, die innerhalb einer Docker basierten<br />

Systemumgebung abläuft.<br />

Wir widmen uns der Entwicklung der Microservices mit<br />

NODE.JS und Python sowie der Orchestrierung dieser Services<br />

mit Docker Images und Docker Container. Schließlich zeigen<br />

wir auf, wie diese Microservices innerhalb einer Continuous<br />

Delivery Pipeline* automatisiert getestet werden, um sicherzustellen,<br />

dass die Dienste wie erwartet funktionieren.<br />

* Ein Wort in eigener Sache: Um die Lesbarkeit dieses Artikels zu erhöhen, werden<br />

englische Fachbegriffe nur übersetzt, sofern es möglich ist und nicht zu Missverständnissen<br />

oder Unworten führt. Es wird also aus einer Continuous Delivery Pipeline<br />

niemals eine Kontinuierliche Lieferungsrohrleitung und aus einem Consumer<br />

Contract nie ein Verbrauchervertrag, aber aus einem Testcase durchaus ein Testfall.<br />

Microservices<br />

Der Trend in der Systemarchitektur geht weg vom Monolithen,<br />

hin zu Microservices, um durch kleine überschaubare Programmeinheiten<br />

Continuous Delivery zu ermöglichen und Features<br />

schneller dem Markt zur Verfügung stellen zu können.<br />

Microservices ist ein Architekturmuster, bei dem die Anwendung<br />

in Services unterteilt wird und nicht in Schichten (s.<br />

Abb. 1). Die Microservices sind dabei entkoppelt und laufen in<br />

einem eigenen Prozess, damit diese leichter austauschbar sind.<br />

JS-3-17 – grötz – k1<br />

Microservices sind auch technologisch voneinander unabhängig,<br />

da sie durchaus mit verschiedenen Programmiersprachen,<br />

Frameworks oder Datenbanken realisiert werden können.<br />

Martin Fowler bezeichnet „Microservices“ als „yet another<br />

new term on the crowded streets of software architecture” [Fow14].<br />

Sie lassen sich unter anderem durch folgende Eigenschaften<br />

beschreiben:<br />

Abb. 1: Architekturmuster<br />

H Die Anwendung wird in kleine Services unterteilt, die entkoppelt<br />

sind, und verwendet diese als Komponenten. Diese<br />

Services sind IT-Repräsentationen von in sich abgeschlossenen<br />

fachlichen Funktionalitäten.<br />

H Microservices enthalten alle Schichten (Datenbank, Middleware<br />

und Frontend) einer Funktionalität. Anwendungen<br />

werden somit nicht in Schichten unterteilt, sondern in Funktionalitäten.<br />

H Microservices werden eigenständig und nicht als gesamte<br />

Anwendung deployed. Dadurch entsteht selbst bei der Implementierung<br />

keine Abhängigkeit zu anderen Services.<br />

H Die Kommunikation von Microservices erfolgt meist über<br />

HTTP-basierte Schnittstellen, wie beispielsweise RESTful-<br />

APIs.<br />

Die Idee von Microservices entspricht der Unix-Philosophie<br />

„Schreibe Computerprogramme so, dass sie nur eine Aufgabe<br />

erledigen und diese gut machen.“ [Wiki]<br />

Anforderung mobiler Anwendungen<br />

Die Einführung einer auf Microservices basierenden Architektur<br />

bedarf einiger spezifischer Überlegungen, wenn es darum<br />

geht, die Inhalte für mobile Anwendungen zur Verfügung zu<br />

stellen. Die wichtigste, die Performanz, werden wir in diesem<br />

Artikel auch berücksichtigen.<br />

Webanwendungen werden in der Regel auf Geräten ausgeführt,<br />

die über WLAN oder Ethernet mit dem Internet verbunden<br />

sind, also mit geringer Latenz. Mobile Geräte verbinden<br />

sich oft über 3G, Edge oder auch GPRS, also mit hoher Latenz.<br />

Daher ist es gerade bei solchen Verbindungen wichtig, die Anzahl<br />

der Aufrufe über das Netzwerk gering zu halten.<br />

Da die Anzahl der Aufrufe wachsen kann, trägt jede HTTP-<br />

Transaktion in der Gesamtreaktionszeit zu einem exponentiellen<br />

Wachstum bei. Daher ist es ratsam, hier einen eigenen<br />

Microservice – ein API Gateway [NGINX] – zur Verfügung<br />

zu stellen, der die Daten aller Zugriffe sammelt und in einer<br />

Transaktion an die aufrufende Anwendung übergibt.<br />

Beispiel einer mobilen Microservice-Landschaft<br />

In vorherigen Abschnitt führten wir den Begriff Microservice<br />

ein und präsentierten einige Überlegungen für mobile Anwen-<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

www.javaspektrum.de 1


IntegrationsSpektrum<br />

JS-3-17 – grötz – k1<br />

<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

dungen. Nun werfen wir einen Blick auf die Entwicklung von<br />

Microservices. Das Ganze am Beispiel einer mobilen Anwendung<br />

für Hobby-Angler.<br />

Die Probleme eines Hobby-Anglers<br />

Es ist ein großer Vorteil für Angler zu wissen, wann die Sonne<br />

auf und wann die Sonne untergeht. Warum? Weil auf manchen<br />

Gewässern das Nachtfischen verboten ist. Nun stellt sich die<br />

Frage, von wann bis wann ist es eigentlich Nacht? Die Antwort<br />

steht im Fischereigesetz: eine Stunde nach Sonnenuntergang<br />

bis eine Stunde vor Sonnenaufgang. Daher muss man wissen,<br />

wann die Sonne auf und untergeht.<br />

Dann gibt es ja auch noch die Wetterfrage.<br />

Was zieht der Angler an? Welche<br />

Kleidung muss berücksichtigt werden?<br />

Überrascht mich wieder das Wetter und<br />

ich muss den Anglertrip abbrechen?<br />

All diese Fragen beantwortet unsere mobile<br />

App SunRiseSet (s. Abb. 2). Sie liefert<br />

für einen eingegebenen Ort den Sonnenauf-<br />

und Untergangszeitpunkt, die Wetterdaten<br />

und die Landkarte. Ausgehend von<br />

den Millionen von registrierten Fischereikartenbesitzern<br />

(und noch mehr Schwarzfischern),<br />

wird diese App sicher ein großer<br />

Erfolg. Aus diesem Grund müssen sich Benutzer<br />

registrieren, damit wir mit den Kundendaten<br />

Geld verdienen können.<br />

Abb. 2: Startscreen Soweit die Theorie. Doch beschäftigen<br />

der App<br />

wir uns nun mit der Praxis: der Microservice-Architektur,<br />

die alle diese Daten ermittelt,<br />

speichert und zur Verfügung steht.<br />

SunRiseSet – Microservice-Architektur<br />

Unser System besteht auf der Backend-Seite aus sechs Microservices,<br />

die in Docker-Containern laufen. Der Location-<br />

Service und der Weather-Service greifen auf externe APIs zu.<br />

Die Services sind ein Mix aus NODE.JS und Python und haben<br />

folgende Funktionalitäten:<br />

H API-Gateway dient als Facade. Nur über dieses Gateway<br />

kann auf die dahinterliegenden Service zugegriffen werden.<br />

H Auth dient zu Authentifizierung des User und der Tokenverwaltung.<br />

H MariaDB dient zum Persistieren der Userdaten.<br />

H Location dient zur Ermittlung der Geolocation via externen<br />

Webservice.<br />

H Weather ermittelt die Wetterdaten für die Geolocation via<br />

externen Webservice.<br />

H Sun berechnet Sonnenauf- und Sonnenuntergang für die<br />

Geolocation.<br />

Jedes Service läuft in einem eigenen Docker-Container und<br />

liefert JSON zurück.<br />

Ablauf<br />

Das API-Gateway nimmt Anfragen von Clients (Mobile und<br />

Web) entgegen, führt die Authentifizierung durch und greift<br />

auf die dahinterliegenden Services zu. Abbildung 3 enthält das<br />

Komponentendiagramm, das Sequenzdiagramm in Abbildung<br />

4 zeigt die Details des Login-Vorgangs.<br />

Abb. 3: Übersicht der Docker-Container & Microservices<br />

Abb. 4: Login-Vorgang<br />

Der Login-Service<br />

Unser erster Microservice ist für das Login über das API-Gateway<br />

zuständig. Er besteht gerade mal aus 23 Zeilen (20 ohne<br />

Leerzeilen) NODE.JS-Code, verwendet Port 8080, HTTP und<br />

liefert JSON zurück.<br />

20 Zeilen Code – in Österreich würde man sagen „Ned<br />

schlecht Herr Specht“, oder anders ausgedrückt „Gut gemacht,<br />

bemerkenswert!“<br />

Zur Vereinfachung werden wir uns im Folgenden nur um<br />

diesen Microservice kümmern. Das gesamte Projekt finden Sie<br />

auf GitHub [GitLab]. Wir werden keine Zeit damit verbringen,<br />

den Code der Microservices zu diskutieren.<br />

var express = require('express');<br />

var request = require('request');<br />

var cors = require('cors');<br />

var app = express();<br />

app.use(cors());<br />

app.get('/login', function(req, res) {<br />

request('http://auth:8080/token?username=' + req.query.username +<br />

'&password=' + req.query.password, function(err1, r1, data) {<br />

var _data = {};<br />

try {<br />

var _data = JSON.parse(data);<br />

if(_data.success === true) {<br />

return res.send(_data);<br />

} else {<br />

return res.status(403).send(_data)<br />

}<br />

} catch(e) {<br />

console.error('could not parse auth response', e);<br />

return res.sendStatus(500);<br />

}<br />

});<br />

});<br />

Listing 1: Authentication-Service<br />

Der Aufruf des Service im Browser via<br />

http://localhost:8000/login?username=rgroetz&password=test123<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

2<br />

JavaSPEKTRUM 3/2017


IntegrationsSpektrum<br />

JS-3-17 – grötz – k1<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

sollte folgenden JSON-String zurück liefern:<br />

{"success":false,"errorCode":4000,"message":"invalid credentials"}<br />

Momentan aber liefert der Aufruf noch die Fehlermeldung:<br />

localhost refused to connect.<br />

zurück, da die Services noch nicht in den Docker-Containern<br />

laufen. Zu Docker und wie wir die Microservice-Architektur<br />

zum Laufen bringen, kommen wir im nächsten Abschnitt.<br />

Dockerized Microservice – Build, Ship, Run<br />

Im Vergleich zu klassischen monolithischen Anwendungen<br />

sind die einzelnen Mikroservices für sich gesehen jeweils nicht<br />

sonderlich komplex (siehe 20 Zeilen Code). Einen großen Overhead<br />

bringt jedoch die für die Mikroservices benötigte Infrastruktur<br />

mit sich. Anstatt einer Systemumgebung (Application<br />

Server, Datenbank, …) für die gesamte Anwendung muss für<br />

jeden Microservice eine eigene Systemumgebung installiert,<br />

konfiguriert und betreut werden.<br />

Mit Docker [Tie15] können die einzelnen Microservices inklusive<br />

der benötigten Abhängigkeiten in leichtgewichtige<br />

Container gepackt werden. Docker macht es einfach, die Software<br />

zu paketieren, zu verteilen und auszuführen. Mit Docker<br />

können Tests im Container ausgeführt und in verschiedensten<br />

Umgebungen isoliert getestet werden.<br />

Zusammen mit Cloud-Plattformen besteht jetzt eine einfache<br />

Möglichkeit, das Testen auf mehreren Plattformen vorhersehbar<br />

zu machen. Dies gibt das Vertrauen, das, wenn etwas<br />

auf einer Systemumgebung funktioniert, es auch in der Produktionsumgebung<br />

funktioniert. Die Aussage „Works on my<br />

machine!“ bekommt dadurch gleich wieder eine andere Bedeutung.<br />

Erstellung eines Docker Image<br />

Ein Docker Image ist ein Softwarepaket, bestehend aus allen<br />

Abhängigkeiten und deren Binärdateien. Der Start eines Docker<br />

Image instanziiert einen Docker Container, welcher dann<br />

eine komplette Laufzeitumgebung, wie vom Ersteller des<br />

Image definiert, zur Verfügung stellt.<br />

Anhand eines Dockerfile, welches alle Kommandos enthält,<br />

die zum Bau des Image notwendig sind, kann die Erstellung<br />

des Docker Image automatisiert werden. Listing 2<br />

zeigt das Dockerfile für unser Image, das in weiterer Folge<br />

die Laufzeitumgebung für den Login-Microservice zur Verfügung<br />

stellt.<br />

FROM node:6.9-alpine<br />

COPY ./src /data<br />

WORKDIR /data<br />

RUN npm install && \<br />

npm cache clean && \<br />

rm -rf /tmp/*<br />

EXPOSE 8080<br />

CMD ["node", "server.js"]<br />

Listing 2: Das Dockerfile<br />

Das Dockerfile aus Listing 2 weist Docker an:<br />

H Baue das Image initial mit einem NODE.JS 6.9 Image.<br />

H Kopiere das Verzeichnis ./src/data in das aktuelle Image-<br />

Verzeichnis.<br />

H Definiere /data im Image als das Arbeitsverzeichnis.<br />

H Installiere die NODE.JS-Applikation (unser Microservice)<br />

aus dem Arbeitsverzeichnis.<br />

H Mache Port 8080 nach außen verfügbar.<br />

H Starte die Applikation server.js (unser Microservice).<br />

Mit dem Docker-Kommando:<br />

$ docker build -t facade-login .<br />

starten wir den Bau des Docker Image im lokalen Arbeitsverzeichnis.<br />

Mit Parameter -t geben wir dem Image einen Namen.<br />

So, jetzt haben wir ein Docker Image, das wir in weiterer Folge<br />

zu einem Docker Container machen und in unsere Registry<br />

(registry.gitlab.com/devopsbusters) hochladen und dann für unser<br />

Docker-Compose brauchen. Zur Vereinfachung werden wir<br />

uns nur um den Bau dieses Docker Image in diesem Artikel<br />

kümmern.<br />

Start eines Docker-Containers<br />

Die Schritte, die zum Installieren und Starten eines Mikroservice<br />

in einem Docker-Container notwendig sind, werden über<br />

eine Docker-Compose-Datei automatisiert. So ist es mit Docker-Compose<br />

möglich, die Erstellung der Images zu automatisieren,<br />

und die Erstellung von Docker-Containern kann in jedes<br />

Build-Tool integriert werden.<br />

Die Aufgabe von Docker-Compose ist das Management einer<br />

Gruppe von Docker-Containern. Die Deklaration einer<br />

Gruppe wird in der Datei docker-compose.yml abgelegt. Mit<br />

dem Werkzeug können nun einzelne Container oder Gruppen<br />

erzeugt, verwaltet und gestartet werden.<br />

Eine detaillierte Einführung in Docker, seine Tools und sein<br />

Build-, Ship-, Run-Konzept finden Sie hier: www.docker.com/.<br />

Listing 3 zeigt die Docker-Compose-Datei unserer Microservice-Landschaft.<br />

version: '2'<br />

services:<br />

sun:<br />

build: ../service-sun<br />

image: registry.gitlab.com/devopsbusters/service-sun:master<br />

ports:<br />

- "8001:8080"<br />

location:<br />

build: ../service-location<br />

image: registry.gitlab.com/devopsbusters/service-location:master<br />

ports:<br />

- "8002:8080"<br />

weather:<br />

build: ../service-weather<br />

image: registry.gitlab.com/devopsbusters/service-weather:master<br />

ports:<br />

- "8003:8080"<br />

auth:<br />

build: ../service-auth<br />

image: registry.gitlab.com/devopsbusters/service-auth:master<br />

ports:<br />

- "8004:8080"<br />

login:<br />

build: ../facade-login<br />

image: registry.gitlab.com/devopsbusters/facade-login:master<br />

ports:<br />

- "8005:8080" <br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

www.javaspektrum.de 3


IntegrationsSpektrum<br />

JS-3-17 – grötz – k1<br />

<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

links:<br />

- auth<br />

api:<br />

build: ../facade-api<br />

image: registry.gitlab.com/devopsbusters/facade-api:master<br />

ports:<br />

- "8000:8080"<br />

links:<br />

- auth<br />

- sun<br />

- location<br />

- weather<br />

Listing 3: Docker-Compose-Datei<br />

Mit dem Befehl:<br />

docker-compose build<br />

bauen wir die Container für unserer Microservice-Umgebung.<br />

Mit dem Befehl:<br />

docker-compose up -d<br />

werden die Container gestartet und die Services zur Verfügung<br />

gestellt.<br />

Up & Running?<br />

So, die Microservices sind codiert, die Docker Images gebaut,<br />

die Microservices laufen in den Docker-Containern. So unsere<br />

Vermutung. Doch stimmt unsere Vermutung? Mit:<br />

docker ps<br />

prüfen wir, welche Docker-Container gestartet sind. Und siehe<br />

da, sechs Microservices laufen wie definiert (s. Abb. 5).<br />

Abb. 5: Die laufenden Microservices<br />

Nun prüfen wir noch auf leichte Art und Weise via Browser,<br />

dass die Services laufen und korrekt zusammenarbeiten:<br />

http://localhost:8005/login?username=rgroetz&password=test123<br />

Und siehe da, unsere Microservices laufen (s. Abb. 6)! Im Gegensatz<br />

dazu liefert der Aufruf mit einem gültigen User folgende<br />

JSON-Nachricht zurück (s. Abb. 7).<br />

Nachdem wir unsere Services nicht immer manuell testen<br />

wollen, kümmern wir uns jetzt um automatisierte Tests, die in<br />

unserer Continuous Delivery Pipeline [Fow13] laufen sollen.<br />

Testen von Microservices<br />

Eine Microservice-Architektur besteht aus unabhängig einsetzbaren<br />

Diensten, die es erschweren, nur altbekannte Testansätze<br />

in der Pipeline anzuwenden.<br />

Durch das Zerteilen eines Systems in kleine, eigenständige<br />

Services werden zusätzliche Schnittstellen sichtbar, die in einer<br />

Abb. 6: Login-Versuch mit ungenügenden Berechtigungen<br />

Abb. 7: Login-Versuch mit ausreichenden Berechtigungen<br />

monolithischen Architektur nicht vorhanden waren. Zusätzliche<br />

Schnittstellen, nicht nur in technischer, sondern auch in<br />

organisatorischer Hinsicht, sind hilfreich, sind doch nun meistens<br />

mehrere Teams an der Entwicklung beteiligt.<br />

Die Zusammenarbeit mehrerer Services, sowohl aus Consumer-<br />

als auch Provider-Sicht, und die reibungslose Integration<br />

der Services untereinander sind daher ein wichtiger Aspekt.<br />

Diesem Umstand wird durch zusätzlichen Tests, besser bekannt<br />

unter dem Begriff „Consumer-Driven Contracts“, begegnet.<br />

In der Welt der Softwaretests gibt es viele Bezeichnungen<br />

für die verschiedensten Arten von Tests. Eine strikte Trennung<br />

nach Zielen und Methoden ist sehr schwer. Das ISTQB-Glossar<br />

[ISTQB] alleine unterscheidet fünf verschiedene Arten von<br />

Acceptance-Tests. Diverse Testpyramiden, verschiedenster<br />

Testspezialisten [Scott, Ash14] tragen nicht dazu bei, hier Unklarheiten<br />

auszuräumen. Zum Test von Microservice-Umgebungen<br />

sollten unserer Meinung nach folgende Tests Anwendung<br />

finden.<br />

Komponententests<br />

Unit- oder Komponententests sind immer noch anwendbare<br />

Testansätze, denn innerhalb seiner Grenzen ist jeder Service noch<br />

sehr zusammenhängend. Diese Tests durchlaufen kleine Code-<br />

Abschnitte, also Klassen oder Methoden. Abhängige Komponenten<br />

werden dabei durch Mocks ersetzt, um den Service in einer<br />

isolierten Umgebung unabhängig testen zu können.<br />

Integrationstests<br />

Durch die zusätzlichen Schnittstellen werden Integrationstests<br />

noch wichtiger für das Gesamtgefüge. Wurden bisher die Daten<br />

innerhalb eines Monolithen ausgetauscht, kommt durch<br />

Microservices der Datenaustausch, in den meisten Fällen via<br />

HTTP, ins Spiel. Die Integrationstests sollen sicherstellen, dass<br />

dieses Zusammenspiel funktioniert.<br />

Consumer-Driven Contracts<br />

H it Consumer-Driven Contracts (CDC) ist ein fast vergessener<br />

Testansatz wieder in den Mittelpunkt gerückt.<br />

Thoughtworks hat aufgrund des Microservice-Hypes Consumer<br />

Driven Contracts wieder auf den Technology Radar<br />

[ThoughtWorks] zurückgeholt:<br />

„… We’ve decided to bring consumer-driven contract testing back<br />

from the archive for this edition even though we had allowed it to<br />

fade in the past. The concept isn’t new, but with the mainstream<br />

acceptance of microservices, we need to remind people that consumerdriven<br />

contracts are an essential part of a mature microservice testing<br />

portfolio, enabling independent service deployments …“<br />

Consumer-Driven Contracts betrachten Microservices aus<br />

der Perspektive der aufrufenden Einheit. Der Contract beschreibt,<br />

wie die Antwort des Providers aufgebaut sein muss.<br />

Im Grunde genommen definiert der Consumer eines Service<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

4<br />

JavaSPEKTRUM 3/2017


IntegrationsSpektrum<br />

JS-3-17 – grötz – k1<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

zusammen mit dessen Provider einen Kontrakt und prüft in<br />

einem Test, ob dieser eingehalten wird. Stellt der Provider nun<br />

eine neue Version des Service zur Verfügung, stellt er durch<br />

den Contract-Test aller Consumer sicher, dass alle Serviceaufrufe<br />

der Consumer noch funktionieren.<br />

End-to-End-Tests<br />

End-to-End-Tests kommen zum Einsatz, um die volle Qualität<br />

des Systems zu gewährleisten. Diese Tests prüfen, ob die<br />

Geschäftsfälle, wie sie ein Anwender durchführt, das richtige<br />

Ergebnis liefern. Es wird dabei sichergestellt, dass Systemabhängigkeiten<br />

und Datenintegrität zwischen verschiedenen<br />

Systemkomponenten und Systemen beibehalten werden.<br />

Lasttests<br />

Lasttests sind eine Unterkategorie von Performanztests und prüfen,<br />

ob die Leistung der Software unter ungünstigen und extremen<br />

Bedingungen zufriedenstellend ist. Diese Bedingungen<br />

können als Folge von starkem Netzwerkverkehr, Prozessorauslastung<br />

oder Überlastung einer bestimmten Ressource auftreten.<br />

Fazit<br />

Der Trend in der Systemarchitektur geht weg vom Monolithen,<br />

hin zu Microservices, um durch kleine überschaubare<br />

Programmeinheiten Continuous Delivery zu ermöglichen und<br />

Features schneller dem Markt zur Verfügung stellen zu können.<br />

Durch das Zerteilen eines Systems in kleine, eigenständige<br />

Services werden zusätzliche Schnittstellen sichtbar, die in einer<br />

monolithischen Architektur nicht vorhanden waren. Diesem<br />

Umstand muss mit altbekannten und neuen Arten von Tests<br />

innerhalb der Continuous Delivery Pipeline begegnet werden.<br />

Im nächsten Teil dieser Artikelserie widmen wir uns dieser Herausforderung<br />

und gehen näher auf die Implementierung dieser<br />

automatisierten Tests ein.<br />

Links<br />

[Ash14] St. Ashman, Post, 28.12.2014,<br />

http://qa-matters.com/2014/12/28/layers-of-test-automation/<br />

[Fow13] https://martinfowler.com/bliki/DeploymentPipeline.html<br />

[Fow14] https://martinfowler.com/articles/microservices.html<br />

[GitLab] https://gitlab.com/devopsbusters<br />

[ISTQB] https://www.astqb.org/glossary<br />

[NGINX] API-Gateway Pattern, https://www.nginx.com/blog/<br />

building-microservices-using-an-api-gateway/<br />

[Scott] Blog von A. Scott,<br />

https://watirmelon.blog/tag/testing-pyramid/<br />

[ThoughtWorks] https://www.thoughtworks.com/radar/techniques/<br />

consumer-driven-contract-testing<br />

[Tie15] F. Tiersch, Docker – Teil 1, 17.8.2015,<br />

https://www.ab-heute-programmieren.de/docker-teil-1-was-ist-docker/<br />

[Wiki] https://de.wikipedia.org/wiki/Unix-Philosophie<br />

Rudolf Grötz, seit 30 Jahren in der IT unterwegs<br />

und passionierter Softwaretester, ist seit 2016 als<br />

Testarchitect/Technical Test Analyst bei s-ITSolutions in<br />

Wien im Bereich Softwaretests tätig und lebt den Leitspruch<br />

„Testautomation is not an act, Testautomation<br />

is a habit!“ Neben Fachartikeln in diversen Magazinen<br />

versorgt er die Community auch mit Konferenzauftritten<br />

und organisiert das Vienna Agile Testautomation<br />

Meetup. E-Mail: rudolf.groetz@gmail.com<br />

Dominik Kukacka, bekennender „Clean Coder“,<br />

entwickelt schon seit seiner frühesten Kindheit mit<br />

Begeisterung Software. Seit 2015 verantwortet er als<br />

CTO bei apilayer in Wien die Planung und Umsetzung<br />

der kompletten Service-Landschaft.<br />

Nebojsa Nikolic ist seit mehr als 20 Jahren als<br />

Oracle-DBA und Systemadministrator tätig. Seit 2016<br />

bei SVC designing E-Health in Wien angestellt, gehört<br />

es dort zu seinen Aufgaben, Microservices und Virtualisierung<br />

voranzutreiben, um dem DevOps-Gedanken<br />

gerecht zu werden. Privat ist er leidenschaftlicher<br />

Angler.<br />

Die Meinung in diesem Artikel ist die Meinung der Autoren und<br />

gibt nicht unbedingt die Meinung deren Arbeitgeber wieder!!!<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

17<br />

18<br />

19<br />

20<br />

21<br />

22<br />

23<br />

24<br />

25<br />

26<br />

27<br />

28<br />

29<br />

30<br />

31<br />

32<br />

33<br />

34<br />

35<br />

36<br />

37<br />

38<br />

39<br />

40<br />

41<br />

42<br />

43<br />

44<br />

45<br />

46<br />

47<br />

48<br />

49<br />

50<br />

51<br />

52<br />

53<br />

54<br />

55<br />

56<br />

57<br />

58<br />

59<br />

60<br />

61<br />

62<br />

63<br />

64<br />

65<br />

www.javaspektrum.de 5

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!