Mocks in der Testumgebung (Teil 3)

Im zweiten Teil haben wir gesehen, wie man mit Hilfe eines Mock-Framework (in diesem Fall Moq) eine Anwendung so anpassen kann, dass bestimmte Schnittstellen für Tests gemockt werden und somit keine Fehler liefern können.

In diesem Teil erstellen wir eine selbst programmierte Mock-Anwendung und werden mit unserer Heldendatenbank darauf zugreifen. Hierzu verwenden wir „Docker for Windows“, welches seit Windows 10 genutzt werden kann. Vor Windows 10 musste man Hyper-V verwenden, um z. B. eine Linux-Maschine zu simulieren, auf der man Container über Docker laufen lassen konnte. Mit dem neusten Betriebssystem von Microsoft ist dieses Vorgehen auch auf Windows möglich. Man kann sich die neuste Version auf Docs.docker.com herunterladen und installieren. Der dafür heruntergeladene Installer bereitet das System für Docker vor.

Mit Docker starten wir unsere Heldendatenbank – genauso wie die gleich neu erstellte APImock-Anwendung. Mit dem Postman werden wir daraufhin eine POST-Anfrage durchführen, die über die Heldendatenbank in den APImock läuft.

Abbildung 1: Darstellung der Applikation

Visual Studio verfügt über eine Extension, die die Unterstützung mit Docker erlaubt. Beim Erstellen eines neuen Projektes können wir im Fenster für die Vorlagen ein Häkchen setzen, um das Projekt mit Docker für Windows zu kombinieren. Außerdem verwenden wir die Web-API-Vorlage um kein komplett neues Projekt erstellen zu müssen.

Erstellung einer Web-API in Visual Studio
Abbildung 2: Erstellung einer Web-API in Visual Studio

Beim Laden des Projekts wird direkt ein Image im Docker erstellt. Bei jedem neuen Build des Projekts wird das Image in Docker aktualisiert.

Wir können nun zeitgleich mit der PowerShell von Windows die Docker-Befehle ausführen, um unsere aktuellen Images auszulesen.

Von Visual Studio erstellte Images
Abbildung 3: Von Visual Studio erstellte Images

Die Images können, ähnlich wie bei GitHub, in ein Repository für Versionskontrollen und für den Zugriff von verschiedenen Arbeitsplätzen hochgeladen werden. Dafür muss der Tag des Images an den Repository-Namen angepasst und mit dem Push-Befehl darauf hochgeladen werden.

Upload ins Docker Repository
Abbildung 4: Upload ins Docker Repository

Da die Web-API-Vorlage genutzt wurde, sind in der Controller-Klasse bereits die vier Standard-Rest-Befehle (GET, POST, DELETE, PUT) vorgeneriert. Wir löschen alle Funktionen bis auf POST, da es sich hierbei für den Mock um eine „banale Funktion“ handeln soll.

Ausschnitt Code - API-Route der Funktion
Abbildung 5: Codeausschnitt – API-Route der Funktion

Somit haben wir nun eine Anwendung, die nur eine Funktion hat und nichts anderes ausführt. Der Grundgedanke für diese Schnittstelle ist, dass die Datenbank-API den Heldennamen an diese Anwendung schickt, um die Items-Konfiguration für den jeweiligen Helden zu bekommen. Dabei wird die PostMock()-Funktion bei einer POST-Anfrage ausgeführt und der mitgelieferte Heldenname würde für die Anfrage den Algorithmus verwenden. Hier wird allerdings statt eines Algorithmus ein fester String eingesetzt, welcher immer dieselbe Antwort auf jede Anfrage liefert. Somit ist sichergestellt, dass keine Fehler von der neuen Anwendung kommen. So kann auch sichergestellt werden, dass diese Schnittstelle wie gewünscht funktioniert.

Ausschnitt Code: POST-Zugriff auf die API
Abbildung 6: Codeausschnitt: POST-Zugriff auf die API

In der Heldendatenbank-API wurde eine POST-Funktion implementiert, die die Anfrage auf den Heldennamen an die APImock weiterleitet. Dafür wird die WebClient-Klasse verwendet, welche mit einem String (Heldenname) gefüllt wird. Diese wird dann über die Funktion der Klasse UploadValues() weitergeleitet.

Beide Anwendungen sind nun fertig und können im Visual Studio über den Docker-Button gestartet werden. Sobald beide Anwendungen gestartet wurden, kann man sich über die PowerShell die aktuell laufenden Container anschauen. Mit dem Docker-Befehl „docker ps“ erfahren wir auch den Port, auf den die Container laufen.

Ausschnitt aktuell laufende Container
Abbildung 7: Aktuell laufende Container

Wenn wir nun mit dem Postman eine Anfrage auf die Route „api/Helden/MockItems“ der Heldendatenbank-API ausführen, wird diese an den APImock weitergeleitet und wir sehen die Antwort für den Helden „Maria“.

Ausschnitt Schnittstellentest mit Postman
Abbildung 8: Schnittstellentest mit Postman

Die nicht gemockte Anwendung würde in diesem Fall für den Helden „Maria“ die passenden Ausrüstungsgegenstände auflisten. Da es sich hier allerdings um einen Mock handelt, ist der Rückgabe-String „Schwert“ passend zur festen Variable, die wir gesetzt haben.

Somit können nun sämtliche Funktionen getestet werden, welche die Schnittstelle zur APImock nutzen, ohne Fehler von der APImock zu erwarten.

Nun haben wir drei verschiedene Varianten gesehen, wie Mocks eingesetzt werden können:

  1. Das Framework-Moq, welches dafür genutzt wird, Mock-Objekte innerhalb der Anwendung zu erstellen
  2. API-Tools (Postman), die eine Art Mock für das Frontend darstellen
  3. Eine Selbstgeschriebene Anwendung, die als Mock dient

Ich hoffe, ich konnte Ihnen hiermit einen kleinen Einblick in der Welt der Mocks geben.

Mocks in der Testumgebung (Teil 2)

Im ersten Teil wurden Grundlagen zum Mock und das Beispielprojekt vorgestellt. Im zweiten Teil bauen wir einen Mock auf, der unsere Rest-Service-Anwendung interaktiv testet.

Nun kommt es zu der Frage: „Warum sollte ich bei diesem Beispiel einen Mock verwenden?“ Es ist immerhin eine einfache, übersichtliche Anwendung. Wir müssen allerdings beachten, dass wir die Standardfunktionalität der API testen wollen. Wenn wir einen GET- oder POST-Befehl durchführen, wird innerhalb des Rest-Services der komplette Weg zur Datenbank genommen, um auf die Daten, die bereits gespeichert sind, zuzugreifen. An diesem Punkt kommt der Mock ins Spiel. Es wird der Zugriff auf die Datenbank, mit Hilfe des Mocks simuliert. Somit kann sichergestellt werden, dass falls ein Fehler gefunden wird, dieser nichts mit dem Zugriff auf die Datenbank zu tun hat, sondern mit dem Bereich in dem wir den Test durchgeführt haben. Das wäre ein Beispiel für das Anwendungsgebiet. Ein weiteres wäre der Zugriff von außen auf die API. Im Normalfall würde eine solche API ein Frontend zur Eingabe der Informationen besitzen. Da wir allerdings die Pfade kennen, in die die Daten eingespeist werden, können wir ein API-Tool verwenden, welches das Frontend simuliert und spezifische Daten an die API schickt.

Ich werde in diesem Teil auf das Mock-Framework „Moq“ eingehen und das API-Test-Tool „Postman“.

Moq ist ein kleines und einfaches Mock-Framework für .Net oder C#. Es gibt mehrere Framework (NSubstitute, Rhino Mocks, etc …) doch diese sind meist recht kompliziert zu verwenden. Moq sticht in dem Sinne heraus, dass es eine einfache Syntax verwendet und für die meisten Anwendungsgebiete vollkommend ausreichend ist. Das Moq Framework-Package kann über Visual Studio einfach mit dem Nuget-Tool heruntergeladen und direkt in dem Quellcode eingebunden werden.

using System.Net.Http;
using Moq;
using NUnit.Framework;

Hier der einfache Aufbau eines Unit-Tests. Wir erstellen eine neue Heldenliste, welche ihre Daten aus der Datenbank bekommt. Mit diesen Informationen können wir den Service erstellen, der von der API abgerufen wird. Wir rufen den vierten Helden auf, den wir eingespeichert haben. In diesem Fall wäre es „Maria“.

[Test]
  public void TestMethodwithoutMoq()
{
     //Arrange (Create)
   Heldenliste liste = new Heldenliste();
   var heldAPI = new HeldenService(liste);
   string shouldBe = "Maria";
   //Act (Test)
   var ergebnis = heldAPI.GetHeroName(4);
   //Assert (Verify)
   Assert.That(ergebnis,Is.EqualTo(shouldBe),"Is Not as Aspected");
   }
[Route("api/Helden/{number}")]
[HttpGet]
public string GetHeroName(int number)
{
   Held hero = heldenAPI.GetHero(number)
   Return hero.name;
}

Jetzt nehmen wir den bereits vorhandenen Aufbau und ersetzen die Heldenliste mit einem Mock. Somit verwenden wir nicht mehr die Daten, die in der Datenbank gespeichert sind und haben die Anwendung erstmal von der Datenbank getrennt. Mit diesem Aufbau ist garantiert, dass keine Fehler von der Datenbankverbindung den Test behindern.

[Test]
public void TestMethodwithMoq()
{
    Helden mockHero = new Helden()
{
    Name = "Detlef",
    Klasse = "Zauberer",
    Alter = 40,
    Level = 14
};
//Arrange (Create)
var moqHeldenliste = new Mock();
moqHeldenliste.Setup(x => x.GetHero(It.IsAny()))
    .Returns(() => mockHero);
var heldenapi_mock = new HeldenAPI(moqHeldenliste.Object);
var shouldBe = "Detlef";
//Act (Test)
var ergebnis = heldenapi_mock.GetHeroName(It.IsAny());
//Assert (Verify)
Assert.That(ergebnis, Is.EqualTo(shouldBe), "Is Not as Aspectet");
}

Wenn das Moq-Framework integriert ist, kann man die Klasse-„Mock“ verwenden. Bei der Verwendung der Klasse gibt man an welche Klasse simuliert werden soll. Der nächste Schritt ist es dann den Mock mit der Setup()-Funktion zu konfigurieren. Wir geben an, dass egal nach welchem Helden gefragt wird(It.IsAny), immer der mockHero(Detlef) ausgegeben wird. Würden wir den Mock nicht konfigurieren, würde er bei dem Aufruf der GetHeroName()-Funktion einen Default-Wert als Integer nehmen. Bei diesem handelt es sich meist um „0“. Da allerdings keine Daten aus der Datenbank geholt werden, würde er einen Fehler zurückgeben.

Das Moq-Framework lässt sich gut nutzen, um die Funktionalitäten die die API innerhalb ihrer Anwendung aufruft zu testen. Bei dem direkten Aufruf der API kommt sie allerdings an ihre Grenzen. Hier kommt das API-Tool „Postman“ ins Spiel.

„Postman“ kann man als separate Anwendung installieren oder als Chrome-Plugin verwenden. Er ist ausgerüstet mit den meisten REST-Befehlen (GET, POST, PUT, etc…) und weist eine leicht bedienbare Oberfläche auf. Es ist möglich, Testfälle zu schreiben, die während der REST-Befehle ablaufen und mit den gesendeten oder empfangenen Daten arbeiten. Somit bauen wir mit ihm einen Mock, der das Frontend simuliert.

API-Test-Tool „Postman“

Es können „Collections“ erstellt werden, in denen man die REST-Befehle, die wir im letzten Teil verwendet haben, gespeichert werden können. Mit dem POST-Befehl haben wir die Heldin Maria über die API gespeichert. Jetzt wurde noch ein Testfall hinzugefügt, welcher den Antworttext überprüft, den wir von der API zurückbekommen haben. In einem positiven Fall wird zurückgemeldet, dass der gewünschte Held hinzugefügt wurde. In einem Negativ-Fall würden wir eine andere Meldung bekommen und der Testfall würde Failed zurückgeben.

API-Test-Tool „Postman“

Unserer „Collection“ fügen wir auch die Abfrage nach dem letzten zugefügten Helden hinzu. Es können noch viele weitere Abfragen mit eigenen Testfällen der „Collection“ hinzugefügt werden, doch für unser Beispiel reichen diese beiden. Nun ist es möglich die komplette „Collection“ zu starten. Der „Postman“ wird darauf hin nacheinander alle REST-Befehle aufrufen und ihre dazu gespeicherten Testfälle durchgehen. Somit bekommen wir am Ende solch ein Ergebnis:

API-Test-Tool „Postman“

Somit sehen wir, dass der Zugriff von außerhalb, auf die API funktioniert.
Mit diesen Beispielen sehen wir, dass mit dem Moq Framework die Anwendung hinter der API einfach getestet werden kann. Der „Postman“ hingegen vereinfacht, dass testen der API von Seiten des Internets.

Überblick Postman und Moq Framework

Im nächsten Teil werden wir uns weiterhin mit diesem Thema befassen. Dabei werden wir in Docker eine kleine Testumgebung aufbauen, in dem der Rest-Service mit der Datenbank aufgebaut wird, und eine selbst geschriebene Mock Anwendung wird kontinuierlich Zugriff auf die Rest API versuchen. Diese Variante könnte man im späteren Verlauf auch als Lasttest verwenden.

Das was Mocks in der Testumgebung Teil 2 und hier geht es zu Teil 3.

Mocks in der Testumgebung (Teil 1)

Die Komplexität und Interaktion der Softwareanwendungen, die in Unternehmen eingesetzt wird, ist in den letzten Jahren stark gestiegen. Werden heute Releases ausgerollt, steht ein Teil der neuen Funktionen immer im Zusammenhang mit dem Datenaustausch zu anderen Anwendungen. Das merken wir auch im Bereich Softwaretest: Der Fokus der Tests hat sich von reinem Systemtest auf den Bereich der Integrationstests erweitert. Darum kommen auch Mocks zum Einsatz.

Wir arbeiten in einem Testcenter und führen für unsere Kunden den übergreifenden Integrationstest über seine komplexe Anwendungsinfrastruktur durch. Daher bauen wir die Testumgebungen passend zur Produktivumgebung auf. Also wozu brauchen wir dann Mocks, wenn die Integrationstestumgebung bereits alle Software-Komponenten besitzen?

Zum Teil liegt man mit dieser Denkweise schon richtig, allerdings werden auf den unterschiedlichen Staging-Ebenen beim Test verschiedene Fokusse gesetzt. Mocks werden im Systemtest eher weniger verwendet, da in diesem Bereich die Funktionen der Software getestet werden. Dafür werden Mocks häufiger in Integrationstest verwendet. Da es aber nicht immer möglich ist, den kompletten Nachrichtenfluss zu testen, wird als Alternative die Kommunikation an der Schnittstelle geprüft.

Werden im Integrationstest 3 Komponenten getestet, die im Nachrichtenfluss direkt hintereinanderliegen, kann bei Verwendung aller 3 Softwarekomponenten nicht hundertprozentig sichergestellt werden, dass die Prozedur ohne Probleme läuft. Es könnte sein, dass die jeweiligen Fehler der Komponenten den Nachrichtenfluss verfälschen und im Nachhinein wieder richtiggestellt wird, der Fehler quasi maskiert wird. Daher stellen wir Mocks an die jeweiligen Enden der Komponenten, um gezielte Ein- und Ausgaben zu bekommen.

Um die beschriebenen Probleme und Eigenheiten eines Mocks zu erklären, nutzen wir als zu testende Anwendung einen Rest-Services. Der Rest-Service sollte in der Lage sein, ein GET und ein POST–Befehl durchzuführen. Würde unsere Komponente nun nach personenspezifischen Daten beim Mock nachfragen mit einem GET-Befehl, könnten wir unseren Mock so konfigurieren, dass er standardisierte Antworten zurück gibt oder bei POST-Befehlen einfache Berechnungen durchführt.

Mit Microsoft Visual Studio kam man in C# schnell ein WebAPI-Projekt erstellen, welches die grundlegende Funktion eines Rest-Services besitzt. Die Methoden des Controllers müssen nur noch angepasst werden und man hat eine funktionierende Rest-API zur Verfügung.

Datei > Neu > Projekt > Visual C# > Web > Webanwendung

Wenn man sich die Controller der WebAPI anschaut, kann man sehen, dass bestimmte URLs, Funktionen innerhalb der API aufrufen. In diesem Beispiel wird eine kleine WebAPI verwendet, die als Helden-Datenbank genutzt wird.

Heldenliste

Die Route beschreibt den URL-Pfad um die HttpGet(GET-Befehl)- oder HttpPost(POST-Befehl) Funktion aufzurufen.

Beispiel: http:localhost/api/helden/add

Sobald man die Rest-API zum Laufen gebracht hat, ist es mit einem API-Tool(Postman) oder einem Browser möglich, unterschiedliche Rest-Befehle an die URL zu schicken. Wenn nun ein POST-Befehl an die Rest-API geschickt wird, nimmt der Service diesen an und erkennt anhand der URL, dass die Funktion AddHeldenDetails() aufgerufen werden soll. Die Funktion nimmt die mitgeschickten Daten auf und fügt sie ihrer Datenbank hinzu. Als Antwort liefert sie den Return-Wert der Funktion. In diesem Fall die Bestätigung über das Hinzufügen des gewünschten Heldes.

POST-Befehl:

POST /api/Helden/zwei HTTP/1.1
Host: localhost:53521
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: b169b96f-682d-165d-640f-dbcafe52789e
{ "Name":"Maria","Klasse": "Lord","Alter":68,"Level":57 }

Antwort:

Der Held mit dem Namen: Maria wurde hinzugefügt

Wir haben nun mit unserem POST-Befehl die Heldin Maria der Datenbank hinzugefügt. Nun können mit dem GET-Befehl die gespeicherten Helden abrufen werden. Hier ein Beispiel zum Format des GET-Befehls, welcher an die Rest-API geschickt wird mit der dazugehörigen Antwort der API:

Abfrage:

GET /api/helden/get HTTP/1.1
Host: localhost:53521
Content-Type: application/json
Cache-Control: no-cache
Postman-Token: b3f19b01-11cf-85f1-100f-2cf175a990d9

Antwort:

Antwort der API

In der Antwort ist zusehen, dass die Heldin Maria der Liste hinzugefügt wurde und nun jederzeit abgerufen werden kann.

Nun wissen wir um die Funktionsweise den Rest-Services Bescheid, welcher Input zu welchem Output führt. Mit dieser Information können wir uns dann an den Bau eines Mocks begeben. Mit diesem Thema werde ich mich im nächsten Teil genauer befassen.

Das war „Mocks in der Testumgebung Teil 1“ … Teil 2 folgt 🙂