Mock Server betreiben mit Mountebank und Docker

Keine Kommentare

Bei der Entwicklung von Applikationen die von anderen Systemen abhängig sind, z.B. durch die Anbindung von Geschäftslogik und Daten, steht man vor der Frage, wie diese getestet werden sollen, falls die Systeme nicht verfügbar sind. Klassischerweise würde diese Anbindung über Mocks in Unit- oder funktionalen Tests durchgeführt werden. Trotzdem kann auf dem Weg durch die Applikation bis zur Netzwerkschicht einiges schief gehen.

In diesem Artikel betrachten wir eine Lösung, die einen Mock Server für solche allumfassenden Tests bereitstellt. Auf diese Art können Aufrufe durch eine Applikation gegen einen Mock Server durchgeführt und tiefgreifend getestet werden. Dadurch werden allumfassende Applikation und System-Ende-zu-Ende-Tests ermöglicht.

Mountebank

Mountebank ist ein Open Source Tool, das plattformunabhängige Multi-Protokoll Test Doubletten auf der Netzwerkschicht bereitstellt [1]. 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 Protkolle sind HTTP, HTTPS, TCP und SMTP.

Zu diesem Zweck steht eine DSL bereit, die genutzt werden kann, um Imposter Stubs zu konfigurieren, welche statisch oder dynamisch Responses für Requests erstellen [2]. Diese Stub-Funktionalität ist mit einem Proxy Mode erweiterbar, der Aufrufe zu den Originalsystemen aufnehmen und abspielen kann [3], und einem Mock Verfikationssystem, das eine Verfikation von Anfragen zu asynchronen Aufrufen an den Mock Server ermöglicht [4].

In den meisten Fällen reicht ein einfacher Request Response Mock Stub. Solch eine Definition könnte wie folgt aussehen, z.B. für einen HTTP Mock:

{
  "port": 8010,
  "protocol": "http",
  "name": "My Mock",
  "mode": "text",
  "stubs": [
    {
      "responses": [
        {
          headers: {
            'Content-Type': 'text/xml'
          },
          body: "..."
        }
      ],
      "predicates": [
        {
          "and": [
            {
              "equals": {
                "path": "/MyService/MyOperation"
              }
            },
            {
              "contains": {
                "body": "Mountebank"
              }
            }
          ]
        }
      ]
    }
  ]
}

Der Stub würde am Port 8010 auf einen HTTP Call warten. Wenn die Prädikate zutreffen, in diesem Fall ein Aufruf von /MyService/MyOperation welches den String „Mountebank“ im POST Body enthalten würde, wird eine HTTP Response mit dem HTTP Content-Type „text/xml“ und dem Body „…“ gesendet.

Diese Definition kann an Mountebank entweder über das UI, das von Port 2525 erreichbar ist, über die REST API oder während des Applikationsstarts übergeben werden.

Mockdaten-Dateistruktur

Wenn die Applikation gestartet wird, kann eine Konfigurationsdatei automatisch an die Mountebank Instanz über die REST API gesendet werden.

Da solch eine Mockdaten-Datei viele verschiedene Stubs enthalten und dementsprechend groß und kompliziert werden kann, ist eine Vereinfachung notwendig. Diese ist möglich durch die Integration von JavaScript EJS Templating. Es kann verwendet werden, um eine größeren Mock-Datensatz bestehend aus mehreren Dateien und verschiedenen Ordern zu erstellen.

Die Hauptdatei „imposters.ejs“, die eine Liste für den Import von Mock Unterordnern für solch einen großen Mock Datensatz enthalten würde, könnte wie folgt aussehen:

{
  "port": 8010,
  "protocol": "http",
  "name": "NKD Mock",
  "mode": "text",
  "stubs": [
    <% include myServiceA/imposters.ejs %>,
    ...
  ]
}

Dabei würde im Unterordner die Datei „myServiceA/imposters.ejs“, welche die Stubs und Responses definiert, abgelegt werden. Zur weiteren Reduktion der Komplexität können diese wieder in separate Dateien ausgelagert werden:

{
  "responses": [
    {
      "inject": "<%- stringify(filename, 'myServiceA/responseA.ejs') %>"
    }
  ],
  "predicates": [
    {
      "and": [
        {
          "equals": {
            "path": "/MyService/MyOperation"
          }
        },
        {
          "contains": {
            "body": "Mountebank"
          }
        }
      ]
    }
  ]
},
{
  "responses": [
    {
      "inject": "<%- stringify(filename, 'myServiceA/default.ejs') %>"
    }
  ],
  "predicates": [
    {
      "equals": {
        "path": "/MyService/MyOperation"
      }
    }
  ]
}

In diesem Fall haben wir außerdem einen Standardfall, der greift, falls die anderen Prädikate nicht erfolreich evaluiert werden.

Die Response wird als Json-Objekt von eine JavaScript-Funktion aus der Reponse-Datei „myServiceA/default.ejs“ zurück gegeben:

function() {
  return {
    headers: {
    'Content-Type': 'text/xml'
  },
    body: "..."
  };
}

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 Ressourcen effiziente Erzeugen von Virtuellen Maschinen, womit man eine portable, nachvollziehbare und konsistente Infrastruktur aufbauen kann. Dies ist vor allem für die Erstellung, Durchführung und Nachvollziehbarkeit von Test-Szenarien, die infrastrukturbasiert sind, ein großer Vorteil.

Bauen des Mock Servers

Um ein Docker Image für dieses Szenario zu bauen, das die Mock Daten des Mock Servers bereits enthält, benutzen wir ein Docker Image, das Mountebank vorinstalliert hat und im Docker Repository verfügbar ist. Unsere Dockerfile würde wie folgt aussehen:

FROM                    cpoepke/mountebank-basis:latest
 
ADD resources/imposters /mb/
RUN ln -s /usr/bin/nodejs /usr/bin/node
 
EXPOSE 2525
EXPOSE 8010
 
CMD mb --configfile /mb/imposters.ejs --allowInjection

Beim Bauen des Docker Images werden die Mock Daten aus dem Ordner „resources/imposters“ in den „/mb“ Ordner des Docker Images kopiert, die Mountebank Ports der VM freigeschaltet und ein Start Kommando bereitgestellt, das beim Start des Containers Mountebank mit den Mock Daten startet.

Das Docker Image selber wird auf die übliche Weise gebaut:

build --tag="my-ws-mock" .

Starten des Mock Servers

Um den Mock Server mit bereits zugewiesen Ports zu starten, muss man folgendes Kommando ausführen:

run -t -i -p 2525:2525 -p 8010:8010 --name='my-ws-mock' my-ws-mock

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

Weiterführende Arbeiten

Weitere Arbeiten können durch die Integration des Build-Prozesses für dieses Docker Image und das Deployment des Artefakts in ein Repository oder durch eine Continuous Integration/Continuous Deployment Pipeline durchgeführt werden. Daraufhin kann der Mock Server in automatisierten Integrations-, Akzeptanz-, Smoke- oder System-Ende-zu-Ende-Tests verwendet werden, abhängig von der Teststrategie. Sogar größere Szenarien mit verschiedene Mock Servern sind möglich, um verteilte Systeme wie z.B. Microservice basierte Architekturen zu testen, in denen die Mock Server während der Initialisierung der Infrastruktur gestartet werden.

Fazit

In diesem Artikel, haben wir gezeigt, wie ein externer Mock Server mit einem großen Mock Datensatz erstellt werden kann. Dieser Mock Server kann paketiert werden, durch die Verwendung einer Container Technologie wie Docker, und zusätzlich in eine Continuous Integration/Continuous Deployment Pipeline integriert werden. Dies ermöglicht das tiefgreifende Testen einer Applikation durch ihre Schichten bis hin zur Netwerk Schicht hindurch.

Referenzen

[1] http://www.mbtest.org
[2] http://www.mbtest.org/docs/api/stubs
[3] http://www.mulesoft.org/documentation/display/current/Unit+Testing
[4] http://www.mbtest.org/docs/api/mocks
[5] https://www.docker.com/whatisdocker/

Conrad ist Senior IT Consultant 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. 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 Microservices-Community stark, als Gründer und Hauptorganisator des Microservices Meetups Berlin und als Mitglied im Organisations-Komitee der MicroXchg Konferenz.

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

Kommentieren

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