Integrationstests von Mikroservices

By Gerald Mücke | August 10, 2017

Integrationstests von Mikroservices

Integrationstests sind die zweitwichtigste Phase in Continuous Integration und Delivery. Es ist das erste Mal, dass mehrere Komponenten miteinander interagieren. Der aktuelle Trend zu Mikroservice-Softwarearchitekturen erfordert ein neues Denken hinsichtlich der Integrationstests verteilter Systeme. In diesem Artikel möchte ich über die Herausforderungen beim Testen dieser Architekturen reflektieren.

In monolithischen Anwendungen sind Komponenten oder Teile eng gebündelt. Inkompatibilitäten können oft bereits während der Kompilierung erkannt werden. Komponenten interagieren miteinander durch Nachrichten, die lokal übertragen werden, d.h. im selben JVM. Die Nachrichtenübertragung erfolgt meist synchron. Das Typsystem dieser Nachrichten ist in der Regel stark und statisch zwischen den Releases. Netzwerke werden hauptsächlich für LDAP- oder Datenbankverbindungen verwendet.

Grundlegende Infrastrukturmerkmale

Moderne Mikroservice-Architekturen sind anders.

Komponenten sind über verschiedene Knoten verteilt. Jeder Knoten kann eine eigenständige physische Maschine sein, aber auch mit anderen Diensten in einer virtuellen Umgebung koexistieren oder als separater Prozess im selben Betriebssystem laufen. Jede Komponente kann unterschiedliche Bereitstellungsmerkmale wie Skalierbarkeit, Kapazität, Verfügbarkeit, Fehlertoleranz und Leistung aufweisen.

Das Netzwerk ist das Standardmedium für die Kommunikation zwischen Komponenten. Nachrichten, die über das Netzwerk übertragen werden, sind serialisiert und verwenden oft ein Format wie JSON, das keine starke Typisierung erzwingt. Das Format dieser Nachrichten sowie das dahinterliegende Protokoll können sich zur Laufzeit aufgrund der Bereitstellung neuer Komponentenversionen ändern. Der Austausch dieser Nachrichten erfolgt oft asynchron über Warteschlangen mit unterschiedlichen Eigenschaften wie Verfügbarkeit, Größe, Persistenz oder Zustellungsgarantien.

Fehlerschwerpunkte finden

Ein gängiger Ansatz für Tests besteht darin, mit gängigen Fehlerquellen zu beginnen. Und Mikroservice-Architekturen haben davon reichlich.

Die größte Herausforderung von Mikroservice-Architekturen oder verteilten Systemen im Allgemeinen ist die Komplexität. Es gibt gute technische und geschäftliche Gründe, sich für eine solche Architektur zu entscheiden, wie einfachere Bereitstellungen, unabhängige Lebenszyklen, klar definierte Domänengrenzen und Schnittstellen, kürzere Markteinführungszeiten, erhöhte Resilienz oder verbesserte Skalierbarkeit. Ein großes Missverständnis ist jedoch, dass Mikroservices weniger komplex sind.

Tatsächlich ist das Gegenteil der Fall. Die Komplexität hat sich eine Ebene nach unten verschoben, wo sie viel schwieriger zu bewältigen ist. Insbesondere weil mehr technische Komponenten beteiligt sind, die außerhalb des Entwicklungsprozesses liegen, Lebenszyklen von Komponenten entkoppelt sind und das asynchrone Kommunikationsmuster eine völlig neue Klasse von Fehlern einführt.

Darüber hinaus artikuliert das CAP-Theorem eine grundlegende Beschränkung verteilter Systeme: im Grunde können Sie nur zwei von drei Garantien erreichen – Konsistenz, Verfügbarkeit, Partitionstoleranz – für die Datenspeicherung.

Obendrein gibt es die Fallstricke verteilter Computing, die eine Art verteilte-Systeme-Version von Murphys Gesetz sind:

  • Das Netzwerk ist zuverlässig.
  • Die Latenz ist null.
  • Die Bandbreite ist unendlich.
  • Das Netzwerk ist sicher.
  • Die Topologie ändert sich nicht.
  • Es gibt einen Administrator.
  • Die Transportkosten sind null. (nicht nur monetär, sondern auch hinsichtlich Bandbreite, Kapazität, Energie)
  • Das Netzwerk ist homogen.

Verteilte Systeme testen

Während die meisten Entwickler das Testen mit dem Entwickeln automatisierter Tests gleichsetzen, wissen Tester, dass es nicht so ist. Entwickler konzentrieren sich normalerweise auf Funktionalität und darauf zu beweisen, dass sie funktioniert – was in Ordnung ist. Tester versuchen hingegen, neue Erkenntnisse und Informationen aus dem System zu gewinnen, insbesondere indem sie es brechen, nicht um zu zeigen, dass es nicht funktioniert, sondern um zu zeigen, wann und wie das System ausfällt. Mit diesen Informationen können Stakeholder eine fundierte Entscheidung treffen.

Für verteilte Systeme wie die oben beschriebenen Mikroservice-Architekturen wäre der Hauptansatz:

  1. Priorisieren von Geschäftsfähigkeiten – Welche Anwendungsfälle sind am kritischsten?
  2. Identifizieren von Komponenten – Welche Komponenten – Hardware und Software – sind für diese Geschäftsfähigkeiten erforderlich?
  3. Beurteilen, welche Ausfälle in diesen Komponenten auftreten können - es gibt eine unendliche Anzahl von Dingen, die schiefgehen können (auf unendlich viele Arten)
  4. Risikobewertung von Ausfällen – Wie wahrscheinlich ist ein Ausfall und wie groß ist seine Auswirkung?
  5. Beginnen mit Hochrisikoausfällen und versuchen, sie zu induzieren (das ist der knifflige Teil) und beobachten, wie sich das System verhält
  6. Wenn das Verhalten etwas unerwartet ist, die Schritte zur Reproduktion beschreiben und zur Diskussion stellen (könnte ein Fehler, eine emergente Funktion oder einfach nicht spezifiziertes Verhalten sein)

Schritte 1, 2 und 4 sind individuell für jedes Projekt und hängen von vielen Faktoren ab, wie Geschäftszielen, Anforderungen, Technologien, kurz gesagt - dem Kontext.

In den Schritten 3 und 5 ist das Wissen und die Erfahrung des Testers ein entscheidender Faktor.

Zusammenfassung

In diesem Artikel habe ich die Merkmale einer Mikroservice-Architektur aus Testperspektive beschrieben und typische Problemschwerpunkte einer Mikroservice-Architektur diskutiert. Abschließend habe ich den allgemeinen Ansatz zum Testen einer Mikroservice-Architektur beschrieben. In kommenden Artikeln werde ich einen tieferen Blick auf die tatsächlichen Dinge werfen, die schiefgehen können und wie man dafür testet.