Entdecken Sie das TÜV-zertifizierte GoogleTest mit Agentic AI für C/C++-Tests!
Details ansehen »
Whitepaper
Bevor Sie sich näher damit befassen, werfen Sie einen Blick auf die wichtigsten Punkte aus unserem Whitepaper weiter unten.
Als Architektur für den Aufbau komplexer Systeme gewinnen Microservices in der Entwicklergemeinschaft zunehmend an Bedeutung. Obwohl man allmählich erkennt, dass sie kein Allheilmittel für alle Probleme der Anwendungsarchitektur darstellen, können Anwendungen mit ähnlichen Herausforderungen im Zusammenhang mit Abhängigkeiten und Skalierung erheblich davon profitieren.
Mit zunehmender Verbreitung von Microservices wird das Testen dieser Dienste immer schwieriger. Insbesondere das Testen der Schnittstellen zwischen den Diensten gestaltet sich komplex. Es gibt jedoch eine robuste Methode zum Testen von APIs, die die Releases von Microservices verbessert.
In mancher Hinsicht, Testen einer Microservices-Anwendung Das Testen von Microservices unterscheidet sich nicht vom Testen einer Anwendung, die mit einer anderen Architektur entwickelt wurde. Microservices nutzen etablierte Technologien wie REST oder Queues, für die es in der Branche bereits bewährte Testwerkzeuge und Best Practices gibt.
Die besondere Herausforderung bei Microservices liegt in der schieren Anzahl der Dienste, aus denen eine Anwendung besteht, sowie in den Abhängigkeiten zwischen diesen Diensten. Darüber hinaus muss jeder Microservice auch dann einwandfrei funktionieren, wenn die anderen Microservices, von denen er abhängt, nicht verfügbar sind oder fehlerhaft reagieren.
Dieses technische Whitepaper untersucht Teststrategien für Microservices-Architekturen und behandelt Orchestrierungs- und Choreografiemuster, Ansätze zur Servicevirtualisierung sowie Best Practices für die Erstellung automatisierter Tests in komplexen verteilten Systemen.
Der erste Schritt zum Verständnis des Testens von Microservices besteht darin, ein gemeinsames Verständnis der gängigen Nachrichtenmuster zu haben, die ihr Verhalten beschreiben.
Das Grundprinzip von Microservices besteht darin, dass es sich um schlanke, eigenständige Dienste handelt, die als einzeln einsetzbare Prozesse mit der vom Team für sein Projekt als optimal erachteten Technologie ausgeführt werden. Diese Microservices werden häufig Geschäftsfunktionen und Domänen zugeordnet, deren Komponentenzusammensetzung das zu testende System repräsentiert.
Mikrodienste folgen typischerweise zwei Mustern bei der Interaktion miteinander:
Viele Microservices verwenden einen kombinierten Hybridansatz. In diesem Beitrag beschreiben wir einige Herausforderungen, die bei der Erstellung automatisierter Tests für Microservices mit diesen unterschiedlichen Mustern auftreten, und stellen Strategien zu deren Bewältigung vor. Wir konzentrieren uns dabei auf Tests für einzelne Microservices im Gegensatz zu Tests für die gesamte Architektur. End-to-End-Tests der gesamten Anwendung.
Ein Microservice, der Orchestrierung nutzt, führt einen oder mehrere explizite Aufrufe externer Dienste oder Abhängigkeiten durch. Diese Aufrufe erfolgen typischerweise synchron über einen Anfrage-Antwort-Ablauf und greifen häufig auf REST-basierte Dienste zu. Müssen die Dienste in einer bestimmten Reihenfolge aufgerufen werden, erfolgt der Aufruf eines nachfolgenden Dienstes erst, nachdem eine Antwort auf den Aufruf eines vorherigen Dienstes eingegangen ist. Da ein Dienst einen anderen explizit aufruft, sind sie eng gekoppelt.
In diesem Beispiel verwaltet der Portfolio-Service die Aktienpositionen eines Nutzers. Beim Hinzufügen einer Position zum Portfolio ruft der Portfolio-Service den REST-Service „Konten“ auf, um sicherzustellen, dass der Nutzer über ausreichend Guthaben auf seinem Konto verfügt. Nach der Antwort des Konto-Services ruft er den REST-Service „Kurse“ auf, um den aktuellen Aktienkurs abzurufen. Sobald auch dieser Service eine Antwort liefert, kann der Portfolio-Service die neue Position hinzufügen.
Das Erstellen und Ausführen von Tests für den Portfolio-Mikroservice ist aus folgenden Gründen eine Herausforderung:
Eine Lösung besteht darin, die Antworten der Mikrodienste „Konten“ und „Angebote“ mithilfe von Servicevirtualisierung zu simulieren. Service-VirtualisierungslösungMit Tools wie Parasoft Virtualize können Sie virtuelle Versionen der Microservices „Konten“ und „Angebote“ definieren und diese zusammen mit der eigentlichen Instanz des Microservices „Portfolio“ bereitstellen. Die Virtualisierung von Microservices ähnelt der Virtualisierung anderer Service- oder Anwendungsarchitekturen.
Sobald dies geschehen ist, kann der Portfolio-Mikroservice unabhängig von seinen beiden Abhängigkeiten getestet werden.
Die nächste Herausforderung besteht darin, verschiedene Umgebungen für unterschiedliche Anwendungsfälle zu konfigurieren, beispielsweise wenn die Dienste „Konten“ und „Kurse“ erwartetes und unerwartetes Verhalten zeigen. Angenommen, das Team möchte testen, wie sich der Dienst „Portfolio“ verhält, wenn entweder der Dienst „Konten“ oder der Dienst „Kurse“ langsam reagiert oder Fehler ausgibt. Dies erfordert möglicherweise die Durchführung von mindestens fünf verschiedenen Testreihen mit jeweils unterschiedlicher Umgebungskonfiguration.
| Testlauf | Konfiguration des Kontendienstes | Konfiguration des Angebotsdienstes |
|---|---|---|
| Normales Verhalten | Normales Verhalten | Normales Verhalten |
| Langsame Reaktionszeit der Konten | Reagiert langsam | Normales Verhalten |
| Fehlermeldungen für Konten | Antwortet mit Fehlern | Normales Verhalten |
| Angebote langsame Reaktionszeit | Normales Verhalten | Reagiert langsam |
| Fehlermeldungen bei Anführungszeichen | Normales Verhalten | Antwortet mit Fehlern |
Für jeden Testlauf muss die Umgebung korrekt konfiguriert werden, bevor die Tests für diese Konfiguration ausgeführt werden können. In diesem Beispiel ergeben sich mindestens fünf verschiedene Testläufe, von denen jeder seine eigene Umgebungskonfiguration besitzt. Das Modul „Environment Manager“ der Parasoft CTP (Continuous Testing Platform) kann diese verschiedenen Umgebungskonfigurationen verwalten.
Das in diesem Beispiel beschriebene Problem ist nicht spezifisch für eine Microservices-Architektur. Ähnliche Probleme treten generell in serviceorientierten Architekturen sowie in monolithischen Anwendungen auf, die nur von wenigen Diensten abhängen. In einer Microservices-Architektur steigt die Anzahl der abhängigen Dienste jedoch deutlich an. Schon in diesem einfachen Beispiel mit nur drei Microservices wird deutlich, dass die Anzahl unterschiedlicher Umgebungskonfigurationen beim Testen verschiedener Anwendungszustände schnell zunehmen kann. In einer Microservices-Umgebung mit Dutzenden oder Hunderten von Diensten ist die Möglichkeit, verschiedene Umgebungskonfigurationen für unterschiedliche Testszenarien zu erstellen, zu verwalten und programmatisch zwischen ihnen umzuschalten, von entscheidender Bedeutung.
Mit der Weiterentwicklung von Microservices durch Teams sind API-Änderungen unvermeidlich. Ein zentrales Problem dabei ist, die Auswirkungen dieser Änderungen auf die Nutzer der Services zu verstehen. Die Branche setzt daher zunehmend auf Vertragstests. Ein bisher wenig beachtetes Problem im Zusammenhang mit API-Änderungen ist jedoch die effiziente Aktualisierung von Testszenarien und virtuellen Assets innerhalb der Testinfrastruktur, um die aktualisierten APIs abzubilden.
Wenn ein Team die API eines von ihm entwickelten Microservices ändert, müssen alle Tests, die diesen Microservice validieren, entsprechend den API-Änderungen aktualisiert werden. Umgekehrt gilt: Werden virtuelle Dienste verwendet, um abhängige Microservices zu simulieren, und ändert sich die API eines dieser abhängigen Microservices, müssen die virtuellen Dienste für den abhängigen Microservice aktualisiert werden, um die API-Änderungen widerzuspiegeln.
Viele Teams verwenden OpenAPI, RAML oder eine andere Servicedefinition, um die APIs ihrer Microservices zu beschreiben. Bei Verwendung von Servicedefinitionen erkennt das Change Advisor-Modul in Parasoft SOAtest und Parasoft Virtualize automatisch, welche APIs sich geändert haben, und aktualisiert bestehende Funktionstests oder virtuelle Services automatisch mit neuen und/oder entfernten Feldern in der API. Teams können aktualisierte Versionen ihrer Servicedefinitionen erstellen und mit Change Advisor die Auswirkungen der Änderungen auf ihre Tests und virtuellen Services analysieren, bevor sie diese implementieren. Nach der Implementierung ermöglicht Change Advisor eine schnelle und einfache Aktualisierung bestehender Assets, um die Änderungen in den Microservices widerzuspiegeln.
Eines der Hauptziele einer Microservices-Architektur ist die Schaffung unabhängiger Komponenten. Dadurch werden Bereitstellung, Skalierung und Aktualisierung der Dienste vereinfacht. Dieses Ziel wird jedoch bei Verwendung des Orchestrierungsmusters nicht vollständig erreicht, da einzelne Microservices direkte Abhängigkeiten zueinander aufweisen. Eine Lösung bietet das Choreografie-Muster, auch bekannt als „reaktive“ oder „ereignisgesteuerte“ Microservices. Bei diesem Muster referenzieren sich Microservices nicht direkt. Stattdessen senden sie Nachrichten an Ereignisströme, die von anderen Microservices abonniert wurden.
Nehmen wir in diesem Beispiel an, der Portfolio-Service wurde angewiesen, eine Aktienposition hinzuzufügen. Anstatt den Konten-Service direkt aufzurufen, veröffentlicht er ein Ereignis im Ereignisstrom „Position hinzugefügt“. Der Konten-Mikroservice hat diesen Ereignisstrom abonniert und erhält daher die Benachrichtigung. Er prüft, ob der Benutzer über ausreichend Guthaben auf seinem Konto verfügt. Ist dies der Fall, reduziert er den Kontostand und veröffentlicht ein Ereignis im Ereignisstrom „Konto aktualisiert“. Verfügt der Benutzer nicht über ausreichend Guthaben, veröffentlicht er möglicherweise ein Fehlerereignis in einem anderen Ereignisstrom (aus Gründen der Übersichtlichkeit nicht dargestellt). Der Portfolio-Mikroservice ist ebenfalls im Ereignisstrom „Konto aktualisiert“ abonniert. Sobald er das vom Konten-Mikroservice veröffentlichte Ereignis empfängt, aktualisiert er sein Portfolio basierend auf der Bestätigung des Konten-Service.
Die asynchrone Kommunikation in dieser Architektur bietet den Vorteil, dass die Dienste stark voneinander entkoppelt sind – Instanzen jedes Dienstes können ersetzt, neu bereitgestellt oder skaliert werden, ohne dass die anderen Microservices davon betroffen sind. Der Nachteil besteht darin, dass die asynchrone Natur der Ereignisse es schwieriger macht, die Systemausführung und den Ereignisablauf zu verstehen. Abhängig von der Reihenfolge und Art der erzeugten Ereignisse kann sich das System unerwartet verhalten. Dieses sogenannte emergente Verhalten stellt eine inhärente Herausforderung bei der Entwicklung und dem Testen von koordinierten Microservices dar.
Es gibt verschiedene asynchrone Messaging-Muster, die unter die übergeordnete Kategorie der ereignisgesteuerten Microservices fallen.
Das Muster der asynchronen Befehlsaufrufe kommt zum Einsatz, wenn Microservices mithilfe asynchroner Aktionen orchestriert werden müssen – wenn also ein Microservice einen anderen asynchron aufrufen und gleichzeitig sicherstellen muss, dass der zweite Microservice die Nachricht erhalten hat. In diesem Muster werden Nachrichten typischerweise über Warteschlangen ausgetauscht. Ein häufig verwendetes Framework in Microservice-Architekturen zur Implementierung dieses Musters ist RabbitMQ.
Ein konkretes Beispiel für dieses Muster ist, wenn ein Microservice ein Ereignis für einen zweiten Microservice veröffentlicht, dieses verarbeitet und anschließend auf eine Antwort des zweiten Microservices wartet. Dieses Verhalten wurde bereits bei der Einführung des Microservice-Choreografie-Musters beschrieben.
In diesem Beispiel weist ein REST-API-Aufruf den Portfolio-Mikroservice an, eine Position hinzuzufügen. Der Portfolio-Service sendet ein Ereignis an die Warteschlange „Position hinzugefügt“, damit der Konten-Mikroservice es verarbeiten kann. Anschließend wartet er darauf, dass der Konten-Service ein Antwortereignis an die Warteschlange „Konto aktualisiert“ sendet, damit der REST-API-Aufruf die aus diesem Ereignis empfangenen Daten zurückgeben kann. Es gibt zwei verschiedene Möglichkeiten, ein Testszenario für dieses Beispiel zu konfigurieren.
Die Tests müssen so konfiguriert werden, dass sie gleichzeitig ausgeführt werden, sodass das Ereignis vom Accounts-Service gesendet wird, während der Portfolio-Service auf das Ereignis wartet.
Der erste Ansatz ist einfach und erzeugt eine in sich geschlossene Testumgebung ohne zusätzliche externe Abhängigkeiten von der Testinfrastruktur. Der zweite Ansatz ist wiederverwendbar und simuliert das reale Systemverhalten genauer. Allerdings erfordert er die Erstellung, Bereitstellung und Verwaltung einer separaten virtuellen Umgebung.
Eine Variante dieses Musters ist ein Microservice, der in einer Warteschlange auf ein eingehendes Ereignis wartet, das Ereignis verarbeitet und dann ein Folgeereignis in einer anderen Warteschlange veröffentlicht, damit es von einem oder mehreren anderen Microservices verarbeitet werden kann.
In diesem Beispiel ist der Rechnungs-Mikroservice der zu testende Service. Der Zahlungsservice veröffentlicht ein Ereignis in der RabbitMQ-Warteschlange „Zahlung verarbeitet“, das vom Rechnungs-Mikroservice verarbeitet wird. Der Rechnungs-Mikroservice liest das Ereignis aus der Warteschlange, erstellt eine Rechnung und veröffentlicht anschließend ein Ereignis in der Warteschlange „Rechnung erstellt“, um den E-Mail-Mikroservice anzuweisen, dem Kunden eine E-Mail mit der Rechnung zu senden.
Um ein Testszenario für den Invoice-Mikroservice zu erstellen, wird eine Testumgebung konfiguriert, die zwei RabbitMQ-Queues und den bereitgestellten Invoice-Mikroservice enthält. Mithilfe des im IoT/Microservices Pack des Parasoft Marketplace enthaltenen benutzerdefinierten RabbitMQ-Transports wird ein Parasoft SOAtest-Testszenario erstellt, das ein Zahlungsverarbeitungsereignis in der Warteschlange „Zahlung verarbeitet“ veröffentlicht. Anschließend abonniert das Szenario die Warteschlange „Rechnung erstellt“, um zu überprüfen, ob der Invoice-Service das entsprechende Ereignis „Rechnung erstellt“ als Antwort veröffentlicht.
Das Event-Firehose-Muster kommt zum Einsatz, wenn verschiedene Quellen eine sehr hohe Anzahl von Ereignissen erzeugen, die schnell über einen zentralen Hub an verschiedene Konsumenten verteilt werden müssen. In diesem Muster werden Nachrichten über Topics ausgetauscht, während im zuvor beschriebenen Muster der asynchronen Befehlsaufrufe der Nachrichtenaustausch über Queues erfolgt. Ein gängiges Framework zur Implementierung dieses Musters ist Apache Kafka.
Nehmen wir an, wir möchten einen einzelnen Microservice testen, der ein Kafka-Thema abonniert, die empfangenen Ereignisse verarbeitet und anschließend seine Ergebnisse in einem zweiten Kafka-Thema veröffentlicht.
In diesem Beispiel haben wir einen Prognose-Mikroservice, der ein Wetterdaten-Topic abonniert, welches zahlreiche Wetterdaten aus verschiedenen Quellen sammelt. Anschließend verarbeitet er diese Daten, um sein Prognosemodell zu aktualisieren, und veröffentlicht die Prognosedaten im Prognosedaten-Topic. In diesem Fall müssen wir überprüfen, ob der Prognose-Service die erwarteten Ereignisse für einen bestimmten Satz von Wetterdatenereignissen im Prognosedaten-Topic veröffentlicht. Dies geschieht durch die Konfiguration einer Testumgebung mit den beiden Kafka-Topics und dem bereitgestellten Prognose-Service.
Wir würden ein Testszenario mit Parasoft SOAtest und dem im IoT/Microservices Pack enthaltenen benutzerdefinierten Kafka-Transport erstellen. Parasoft Marketplace. Das Testszenario würde zunächst die erforderlichen Ereignisse im Thema „Wetterdaten“ veröffentlichen und anschließend das Thema „Vorhersagedaten“ abonnieren, um zu überprüfen, ob die korrekten Vorhersagedaten vom Vorhersagedienst veröffentlicht wurden. Es müssten mehrere verschiedene Testszenarien erstellt werden, um die verschiedenen Arten und die Reihenfolge der Ereignisse zu überprüfen, die vom Vorhersage-Mikrodienst erwartet werden.
Dies ist ein relativ einfaches Testszenario. Die Tatsache, dass der Forecast-Mikrodienst von den anderen Mikrodiensten entkoppelt ist, hat den glücklichen Nebeneffekt, dass auch der Test für den Forecast-Dienst von den Mikrodiensten entkoppelt ist. In diesem Fall ist keine komplexe Umgebung mit virtuellen Diensten erforderlich. Sie können einfach Testszenarien erstellen, die Ereignisse veröffentlichen, und überprüfen, ob die korrekten Ereignisse als Antwort erzeugt werden.
Viele Microservice-Teams nutzen einen CI/CD-Prozess für die Entwicklung, das Testen und die Bereitstellung containerisierter Microservices, um den Prozess zu automatisieren und die mit Update-Bereitstellungen verbundenen Risiken zu minimieren. Dabei wird automatisch ein Container-Image mit dem Microservice erstellt und in einer Testumgebung bereitgestellt, die häufig von Kubernetes oder einer Kubernetes-basierten Distribution wie OpenShift verwaltet wird. Dort kann der Microservice validiert werden, bevor er in End-to-End-Tests und schließlich in die Produktion überführt wird.
Wir haben gesehen, dass Servicevirtualisierung für die Erstellung von Microservice-Testszenarien nützlich und sogar unerlässlich ist. Wenn Testumgebungen auf Technologien wie Docker oder Kubernetes basieren, müssen die Tools zur Erstellung und Bereitstellung virtueller Dienste nahtlos in diese Umgebungen integriert werden können. Virtuelle Dienste Sie müssen hochgradig komponentenbasiert und leicht bereitstellbar sein, aus denselben Gründen, aus denen die simulierten Microservices komponentenbasiert sind. Traditionelle Anbieter von Servicevirtualisierung erfüllen diese Anforderung jedoch nicht, da die Anwendungen monolithisch, zentralisiert und von mehreren Teams genutzt werden.
Damit die Servicevirtualisierung in diesen Umgebungen funktioniert, müssen Sie containerisierte virtuelle Dienste erstellen, die sich einfach bereitstellen lassen. Um einen containerisierten virtuellen Dienst zu erstellen, verwenden Sie ein Basis-Image, das Folgendes enthält: Parasoft Virtualisieren und alle seine Abhängigkeiten, und darüber ein weiteres Image legen, das alle Konfigurationen der virtuellen Ressourcen für den virtuellen Dienst enthält. Das neue Image für den virtuellen Dienst wird zusammen mit dem Container für den zu testenden Microservice und all seinen (virtualisierten) Abhängigkeiten als Container in der Docker/Kubernetes-Umgebung bereitgestellt.
Die in diesem Artikel beschriebenen Messaging-Muster und zugehörigen Testmuster sind nicht neu, aber der Bedarf an ihrer Anwendung hat mit der zunehmenden Verbreitung von Microservices und der steigenden Anzahl von Anwendungen, die ein Microservices-Paradigma übernehmen, deutlich zugenommen. Parasofts SOAtest, Virtualize und CTP Teams sollen in die Lage versetzt werden, Testszenarien für Microservices mit maximaler Flexibilität zu erstellen und einzusetzen – wodurch die hohe Qualität und Zuverlässigkeit ihrer Microservices sichergestellt wird.
Bereit, tiefer einzutauchen?