Consumer-Driven Contract Testing

Microservice-Umgebungen bieten eine Menge Vorteile gegenüber traditionellen Ansätzen. Unabhängigkeit bei der Wahl der Programmiersprache, unabhängige Deploybarkeit einzelner Services oder die verminderte Service-Komplexität und damit bessere Beherrschbarkeit des (Software-) Gesamtsystems sind nur einige davon. Um davon zu profitieren und nicht viele dieser Vorteile durch die – zugegebenermaßen – erhöhte Vielschichtigkeit der verteilten Infrastruktur wieder zu verlieren, müssen wir in ein paar bisher selten genutzte Methoden investieren. Eine davon ist das Konzept der Consumer-Driven Contracts.

Definition: Consumer-Driven Contracts

CDC Tests sind ausführbare Tests, bei denen ein Servicekonsument Annahmen über Aufrufe (i.d.R. REST und MQ) an (s)einen Serviceanbieter in Form eines Vertrages formuliert. Damit ist sichergestellt, dass, sollten beide Kommunikationspartner in einer Microservicearchitektur grundsätzlich verfügbar sein, sie dieselbe Sprache sprechen. Führt man sich die Aussagen des ersten Absatzes noch einmal vor Augen, ein überaus logischer und wertvoller Testansatz.

Die Vorteile

Wichtig zu verstehen ist, dass dieser Vertrag immer vom Konsument und nicht vom Anbieter aus definiert wird (Consumer-Driven). Der Anbieter weiß somit immer genau wer seine Konsumenten sind. Er merkt sofort wenn er breaking changes vornimmt und erhält Sicherheit bei der Weiterentwicklung seiner Logik. Darüber hinaus sind diese Verträge auch eine Form der Dokumentation. Des Weiteren weiß ein Serviceanbieter sehr genau, welche Funktionalität er seinen Konsumenten bereitstellen muss. Überflüssige Funktionen die das Potential haben mit der Zeit fehleranfällig und übergewichtig zu sein, können dadurch gänzlich eliminiert werden. Zu guter Letzt erhalten beide Parteien, Konsument und Anbieter, frühzeitig Feedback in deren Continuous Integration (CI) Lebenszyklus.

CDC Tests in der Praxis

Eine mögliche Implementierung von Consumer-Driven Contracts stellt Pact dar. Pact wiederum ist für nahezu alle gängigen Programmiersprachen verfügbar. Trotzdem muss nicht zwingend auf dieses Framework zurückgegriffen werden. Eine eigene CDC Implementierung ist ebenfalls denkbar und manchmal auch leichter zu benutzen.

In diesem Artikel wird nachfolgend auf Pact (https://docs.pact.io/) eingegangen. Die hier dargestellten Code-Beispiele zeigen die JVM-Implementierung.

Im Grunde baut Pact auf 2 einfachen Phasen auf: Die erste Phase besteht in der Erstellung eines (Unit-)Tests auf der Konsumentenseite sowie der Bereitstellung des Vertrages. Die folgenden 3 Schritte geben eine weitere Erklärung.

  1. Das Team des Servicekonsumenten definiert die Vereinbarung (der Vertrag) mit dem Serviceanbieter:
.uponReceiving("a root request").method("GET").path("/")
.willRespondWith().status(200).body("Hello, world!")

Übersetzt bedeutet das: Erhalte ich als Serviceprovider einen HTTP-Request des Typs „GET“ auf dem Pfad „/“, soll dieser mit dem HTTP-Statuscode „200 OK“ sowie der Nachricht „Hello, world!“ beantwortet werden. Nicht mehr und nicht weniger.

2. Das Team des Servicekonsumenten definiert nun einen Test gegen diese Annahme. In diesem Fall wären sogar 2 Tests denkbar:

public void shouldRespondWithHelloWorld() throws Exception {
 final Consumer consumer = Consumer.of(mockProvider.getUrl());
 assertEquals(consumer.run().readEntity(String.class), "Hello, world!");
}
public void shouldReturnStatus200() throws Exception {
 final Consumer consumer = Consumer.of(mockProvider.getUrl());
 assertThat(consumer.run().getStatusInfo().getStatusCode(), is(200) );
}

Die erste Ausführung dieser beiden Tests schlägt natürlich noch fehl und liefert ein rotes Ergebnis, da es zu diesem Zeitounkt ja noch keine Implementierung der Logik gibt.

3. Das Team des Servicekonsumenten implementiert anschließend die eigene Funktionalität auf Grundlage der unter 1. und 2. geschaffenen Annahmen.

Im besten Fall ist nun bereits die zweite Ausführung unserer beiden Tests erfolgreich. In einem gedachten CI Lebenszyklus muss sich abschließend noch darum gekümmert werden, dass der gerade definierte Vertrag dem Serviceanbieter zur Verfügung gestellt wird.

Dies leitet gleichzeitig die zweite Phase von Pact-basierten Consumer-Driven Contracts ein.

Einfach gesagt besteht diese Phase darin, dass der Serviceanbieter den Vertrag des Konsumenten (oder natürlich auch die Verträge vieler Konsumenten) gegen seine eigene Logik verifiziert. Dabei werden echte Requests verschickt und die Antworten mit dem Vertrag (oder den Verträgen) abgeglichen. Eine denkbare Integration dieser zweiten Verifikationsphase in die Pipeline des Anbieters besteht entweder aus der Erweiterung der bestehenden Pipeline um einen Consumer-Driven Contract Abschnitts oder ganz und gar einer eigenen CDC-Pipeline. In beiden Fällen muss die wie auch immer geartetet Pipeline vom Build des Konsumenten ausgelöst werden. Dieses Vorgehen stellt sicher, dass alle qualitativen Checks und Tests des Anbieters durchlaufen und die Annahmen des Konsumenten sofort geprüft werden. Im Fehlerfall wird keine neue Version entweder des Konsumenten oder Anbieters deployed und man erhält Gelegenheit, die unterschiedlichen Annahmen zu fixen.

Fazit

Die Vorteile dieses kompletten Ansatzes liegen auf der Hand: Konsument und Anbieter müssen nicht gemeinsam und in zusammenpassenden Version deployed sein um die Entwicklung voranzutreiben. Beide Seiten können getrennt voneinander „offline“ entwickelt und verifiziert werden. Darüber hinaus verringert sich die Gefahr erheblich, dass zueinander inkompatible Versionen von Konsument und Anbieter zur gleichen Zeit laufen und ein Gesamtsystem in einer Fehlerzustand überführen.

Consumer-Driven Contracts eignen sich hervorragend um Schnittstellen (APIs) innerhalb einer Microservice-Architektur zu definieren und zu verifizieren. CI/CD-typisch erhalten beide Seiten so früh wie möglich Feedback über die vorgenommenen Änderungen was letztendlich zu einem qualitativ hochwertigeren Produkt und zufriedeneren Kunden führt.

Seit Version 3 des pact-Formats können neben REST-APIs auch MQ-basierte Kontrakte überprüft werden.

Kommentar schreiben

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