Definition
Contract Testing (auf Deutsch auch “Vertragstest”) ist eine Art von Softwaretest, bei dem die Interaktionen zwischen Softwarediensten überprüft werden. Es bietet eine schnelle und skalierbare Methode, um zu validieren, dass Dienste – etwa innerhalb einer cloud-nativen Anwendung – effektiv miteinander kommunizieren. Auf diese Weise hilft Contract Testing Entwicklern, Probleme zu erkennen, die zu Kompatibilitäts- oder Performanceproblemen führen können.
Um ein optimales Nutzererlebnis zu gewährleisten, müssen Entwickler sicherstellen, dass Softwaredienste gemäß bestimmter grundlegender Anforderungen miteinander interagieren können – etwa indem sie innerhalb eines vorgegebenen Zeitrahmens auf Anfragen reagieren. Contract Testing hilft Entwicklern dabei, sicherzustellen, dass Dienste diese Anforderungen erfüllen. Mithilfe von Contract Testing können Teams automatisch überprüfen, ob die Interaktionen zwischen Diensten den vordefinierten Vereinbarungen entsprechen.
Contract Testing ist eine Methode des Softwaretestens, bei der die Interaktionen zwischen Softwarediensten überprüft werden – mit dem Ziel zu bestätigen, dass diese den Bedingungen eines sogenannten „Contracts“ (auf Deutsch: “Vertrag”) entsprechen.
In diesem Zusammenhang bezeichnet ein Contract eine Reihe von Regeln, die festlegen, wie Dienste miteinander interagieren sollen. Ein Contract kann beispielsweise definieren, welche Arten von Antworten ein Server an einen Client senden darf. Darüber hinaus können Contracts auch Leistungsziele enthalten, etwa dass eine Antwort innerhalb einer bestimmten Anzahl von Millisekunden erfolgen muss.
Viele moderne Anwendungen bestehen aus einzelnen, voneinander getrennten Services, die kontinuierlich miteinander interagieren müssen. Wenn diese Interaktionen nicht wie vorgesehen ablaufen, funktionieren die Anwendungen entweder überhaupt nicht oder bleiben hinter den erwarteten Performance-Anforderungen zurück.
Ein Beispiel: Eine cloud-native Anwendung auf Basis von Microservices kann unter anderem einen Service enthalten, der das Frontend rendert, und einen weiteren, der Daten aus einer Datenbank abruft. Um die Daten, die im Frontend angezeigt werden sollen, zu erhalten, muss der Frontend-Service Anfragen an den Backend-Service senden, der die Daten sammelt und sie anschließend zurück an den Frontend-Service übermittelt. Wenn die Backend-Daten jedoch in einem Format ankommen, das der Frontend-Service nicht verarbeiten kann, führt dies zu einem Fehler – und möglicherweise dazu, dass die Benutzeroberfläche nicht dargestellt wird. Oder wenn die Daten vom Backend zu lange brauchen, um den Frontend-Service zu erreichen, kann dies die Ladezeit der Anwendung negativ beeinflussen und zu einem schlechten Nutzererlebnis führen.
Contract Testing hilft, derartige Probleme zu vermeiden, indem es die Interaktionen zwischen Softwarediensten automatisch überprüft und feststellt, ob sie den Bedingungen eines vordefinierten Contracts entsprechen. Auf diese Weise können Entwickler schnell sicherstellen, dass Softwarekomponenten effektiv und zuverlässig miteinander kommunizieren.
Contract Testing ist umso wertvoller, da sich Softwaredienste häufig ändern – insbesondere in Projekten, die einer Continuous-Delivery-Philosophie folgen, bei der Entwickler regelmäßig Updates implementieren und in Produktionsumgebungen ausrollen. Mit Contract Testing kann ein Entwicklungsteam Tests einfach und effizient vor dem Deployment durchführen, um sicherzustellen, dass Änderungen an einem Dienst nicht dazu führen, dass die Interaktion mit einem anderen Dienst fehlschlägt.
Contract Testing folgt in der Regel diesen Schritten:
- Zu testende Services identifizieren: Zunächst wählt das Entwicklungsteam die Services aus, die getestet werden sollen – typischerweise jene, die regelmäßig miteinander interagieren.
- Contract definieren: Anschließend definieren die Entwickler mithilfe von Code die Bedingungen des Software-Contracts. Diese legen genau fest, welche Service-Interaktionen überprüft werden sollen – z. B. erwartete Anfragen, Antworten oder Leistungsanforderungen.
- Test ausführen: Nachdem der Contract definiert wurde, kann das Team den Test ausführen, indem es im Testumfeld Service-Anfragen oder -Antworten generiert und überprüft, wie die beteiligten Dienste darauf reagieren.
Eine Reihe von Tools steht zur Verfügung, um den Prozess der Contract-Definition und Testdurchführung zu unterstützen. Einige der beliebtesten Lösungen werden weiter unten im Abschnitt zur Implementierung von Contract Testing vorgestellt.
Eine weitere gängige Testform, die Entwickler einsetzen, um die Zusammenarbeit von Services zu überprüfen, ist das Integrationstesting.
Contract Testing und Integrationstesting ähneln sich insofern, als beide die Kommunikation zwischen Services bewerten. In den meisten Fällen beinhaltet Integrationstesting jedoch End-to-End-Tests. Dabei setzen Entwickler eine vollständige Instanz der Anwendung auf – häufig bestehend aus zahlreichen einzelnen Services – und senden dann Anfragen, um zu überprüfen, wie gut das Zusammenspiel dieser Services funktioniert.
Im Gegensatz dazu testet Contract Testing in der Regel die Interaktion zwischen nur zwei spezifischen Services. Im Vergleich zum Integrationstesting bietet Contract Testing folgende Vorteile:
- Einfachheit: Contract Testing erfordert nicht die Bereitstellung einer vollständigen Anwendungsinstanz. Teams müssen nur die spezifischen Services nutzen, die sie testen möchten.
- Geschwindigkeit: Da Contract Tests lediglich die Interaktionen zwischen zwei Services prüfen, liefern sie in der Regel schneller Ergebnisse als Integrationstests.
- Klarere Fehlerursachenanalyse: Durch das gezielte Testen zweier Services lässt sich die Ursache eines Problems – etwa eine langsame Antwort auf eine bestimmte Anfrage – leichter identifizieren. Bei End-to-End-Integrationstests ist die Fehlersuche oft schwieriger, da die Ursache in jedem beliebigen Bestandteil der Anwendung liegen kann.
- Wartungsaufwand: Contract Tests sind einfacher zu pflegen und zu aktualisieren, da sie einen kleineren Umfang haben. End-to-End-Tests müssen oft bei jeder Änderung in der Anwendung angepasst werden – und da sich Anwendungen regelmäßig ändern, müssen Entwickler auch die Integrationstests entsprechend häufig aktualisieren.
Das soll nicht heißen, dass Entwickler keine Integrationstests verwenden sollten oder dass Contract-Tests End-to-End-Integrationstests vollständig ersetzen können. Letztere sind hilfreich, um zu beurteilen, wie Endbenutzer eine Anwendung als Ganzes erleben. Daher werden End-to-End-Integrationstests häufig in den späteren Phasen der Softwareentwicklung ausgeführt, kurz bevor eine Anwendung in Produktion geht. Im Gegensatz dazu bietet Contract Testing eine einfachere und schnellere Möglichkeit, die Interaktionen zwischen bestimmten Teilen einer Anwendung zu testen, und ist somit ein wertvolles Mittel, um Bugs bereits früh im Entwicklungsprozess zu identifizieren.
Contract Testing ist in vielerlei Hinsicht auch mit API-Tests vergleichbar. Tatsächlich betrachten manche Menschen Contract Testing als synonym zu API Testing oder zumindest als eine Unterkategorie davon. Dennoch gibt es einige wichtige Unterschiede zwischen Contract-Tests und anderen Arten von API-Tests.
API-Tests bewerten das Verhalten und die Performance von Application Programming Interfaces, in der Regel, indem geprüft wird, wie ein API-Server auf eine Anfrage eines Clients reagiert. Da APIs regeln, wie Dienste miteinander interagieren, sind API-Tests entscheidend, um sicherzustellen, dass die Kommunikation zwischen Services reibungslos funktioniert.
Contract Testing verfolgt ein ähnliches Ziel, da es ebenfalls die Interaktionen von Services bewertet, die in den meisten Fällen über APIs kommunizieren. Ein wesentlicher Unterschied zwischen diesen Testmethoden besteht jedoch darin, dass sich Contract-Tests in der Regel darauf konzentrieren, zu bestätigen, dass Services in einer Weise interagieren, die mit den Kompatibilitäts- und Performance-Zielen übereinstimmt. API-Testing ist umfassender, da es zusätzlich auch API-Sicherheitsrisiken testen kann.
Darüber hinaus basiert Contract Testing manchmal auf simulierten API-Interaktionen (Mocking). Das bedeutet, dass anstelle einer Anfrage an einen echten API-Client und der Auswertung der tatsächlichen Antwort die Contract-Tests simulieren, wie eine Anfrage oder Antwort basierend auf den Design-Spezifikationen einer API aussehen würde. Durch API-Mocking ist es möglich, Contract-Tests durchzuführen, selbst wenn eine API noch nicht vollständig implementiert wurde. Auch beim API-Testing kann Mocking zum Einsatz kommen, allerdings ist es dort üblicher, „echte“ API-Interaktionen zu testen.
Es gibt zwei Hauptansätze im Contract-Testing: Consumer-driven und Provider-driven.
Consumer-driven Tests
Bei einem Consumer-driven Contract-Test ist der Service-Consumer – also der Dienst, der Anfragen generiert und an einen anderen Dienst sendet – dafür verantwortlich, die Vertragsbedingungen festzulegen und zu prüfen, ob der antwortende Dienst (der sogenannte Provider) diese erfüllt.
Consumer-driven Testing ist besonders vorteilhaft in Projekten, bei denen sich die Consumer häufiger ändern als die Provider. In solchen Fällen können Entwickler bei jeder Aktualisierung eines Consumer-Services Consumer-driven Contract-Tests durchführen, um sicherzustellen, dass durch die Änderungen keine Kompatibilitätsprobleme mit dem Provider entstanden sind. Gleichzeitig lässt sich der Service-Vertrag an neue Anforderungen anpassen, die sich aus den Änderungen am Service ergeben.
Provider-driven Tests
Beim Provider-driven Contract Testing legt der Dienst, der Anfragen empfängt und darauf reagiert – also der Provider – die Vertragsbedingungen fest. Dieser Ansatz ist besonders sinnvoll, wenn sich der Code des Providers häufiger ändert als der des Consumers. In solchen Fällen bietet Provider-driven Testing eine effiziente Möglichkeit, sicherzustellen, dass ein aktualisierter Provider weiterhin wie erforderlich mit den bestehenden Consumer-Services interagieren kann.
Darüber hinaus trägt Provider-driven Testing dazu bei, die Abwärtskompatibilität in Systemen zu gewährleisten, in denen ein Provider mit mehreren Versionen eines Consumers kommunizieren muss. Entwickler können mit Hilfe von Provider-driven Tests überprüfen, ob jede Consumer-Version korrekt mit der neuesten Version des Providers interagiert.
Wie Contract Testing implementiert wird
Um Contract-Testing umzusetzen, müssen Teams zunächst ein passendes Tool auswählen. Beliebte Open-Source-Lösungen sind unter anderem:
- Pact: Pact verfolgt einen Consumer-driven Testing-Ansatz und unterstützt eine Vielzahl gängiger Programmiersprachen.
- Spring Cloud Contract: Spring Cloud Contract konzentriert sich auf Provider-driven Testing. Es ist Java-zentriert und ermöglicht es Entwickler:innen, Tests in Groovy oder YAML zu definieren.
- Dredd: Dredd ermöglicht automatisiertes Contract Testing, indem es API-Dokumentationen auswertet und die Interaktionen von Services daraufhin überprüft, ob sie dem in der Dokumentation definierten Verhalten entsprechen.
- RestAssured: RestAssured ist eine Java-Bibliothek zur Testung von Interaktionen zwischen Java-basierten Services, die RESTful APIs verwenden.
- OpenAPI: OpenAPI (früher Swagger) ist kein Contract-Testing-Tool im engeren Sinne, sondern ein Spezifikationsformat, das definiert, wie Services miteinander interagieren sollen. OpenAPI kann zur Erstellung von Contracts verwendet werden, die sich anschließend mit Tools wie Pact oder Dredd testen lassen.
Durch die Speicherung und Verwaltung von Contract-Tests, die mit spezialisierten Tools erstellt wurden, ermöglicht JFrog eine nahtlose Integration von Contract Testing in den Software-Lebenszyklus. Erfahren Sie mehr – fordern Sie eine Demo an.