Contract-Driven Testing ist eine Vereinbarung zwischen API-Consumer und Provider, die als Tests festhält, wie sich eine Schnittstelle verhält. Der Provider muss diese Tests in der CI bestehen, und aus denselben Tests entsteht ein Mock für Consumer und Tester. In einer großen Bank löst das Systemtests von Integrationsumgebungen, in denen ein kaputter Datenfeed alle Teams blockiert.
Das Wichtigste in Kürze
- Contract-Driven Testing macht aus der Vereinbarung zwischen API-Consumer und Provider Tests, die der Provider in der CI bestehen muss, bevor etwas die Integrationsumgebung erreicht.
- Mocks aus der Vorstellung der Entwickler sind nicht realistisch, und genau deshalb kann 100 Prozent Code Coverage neben furchtbarem Code stehen.
- Dieselben Contract-Tests erzeugen einen Mock, mit dem Consumer und Tester in ihrer Entwicklungsumgebung arbeiten, statt auf 50 integrierte Anwendungen zu warten.
- Phil Royston gewann das Senior Management mit einem einzigen Versprechen, kürzere Testphasen im Release-Zyklus, während die Middle Manager mit den Budgets länger brauchten.
- Freigegebene Business Requirements waren nicht testbar; erst die Warum-Fragen der Tester und eine schmerzhafte Runde mit den Analysten brachten der Bank testbare Anforderungen.
Ein Kernbankensystem, das erst mit 50 anderen Systemen testbar ist
Contract-Driven Testing ist die Antwort, die Phil Royston gefunden hat, als eine große tschechische Bank ihn in ihr Transformationsprogramm holte und er sah, dass kein Team mehr etwas für sich allein testen konnte. Das Kernbankensystem war früher ein eigenständiger Monolith. Ein Tester konnte es isoliert durch den Systemtest schicken. Heute tut dasselbe System nichts, solange es nicht in rund 50 andere Anwendungen integriert ist, und an jedem beliebigen Tag ist eine davon kaputt. Das LDAP-System steht, der Wechselkurs-Feed liefert nicht, der Kundendaten-Feed fällt aus. Damit hängt die ganze Umgebung, und mit ihr jeder Tester, der auf sie angewiesen ist.
Die Bank ist rund 100 Jahre alt. In 35 Jahren musste sie den Weg von Papierprozessen im Kommunismus zu einer Smartphone-App gehen, die sich anfühlen soll wie Revolut: Retail, Corporate, Investment und Private Banking unter einem Dach, mehrere fusionierte Banken in der Geschichte, Millionen Kunden. Die Produktionsqualität ist hoch, und die Leute halten sie hoch, indem sie viel testen. Der Preis dafür sind große Releases, weil alles mit allem verdrahtet ist. DevOps predigt seit 15 Jahren kleine Batch-Größen. Eine Bank in dieser Lage kann keine kleinen Batches fahren. Für eine einzige Änderung müssen zehn Räder gleichzeitig laufen.
Das war der Ausgangspunkt für Phil. Ein Jahr zuvor hatte er den Testprozess der Bank in einem Assessment durchleuchtet und die strukturellen Probleme gesehen. Als das Transformationsprogramm startete, erkannte er den einen Moment, in dem sich ein Teil davon tatsächlich lösen lässt.
Was ist Contract-Driven Testing und wie funktioniert es zwischen Consumer und Provider?
Contract-Driven Testing beginnt mit einer Vereinbarung zwischen dem Consumer einer API und ihrem Provider darüber, wie sich die Schnittstelle verhält, festgehalten als Tests. Wer „Contract“ hört, denkt meist an Felddefinitionen: dieses Attribut ist Pflicht, jenes ist numerisch. Der Contract kann deutlich mehr tragen. Er kann Use Cases enthalten. Ruft der Consumer den Kontoservice mit dieser bestimmten Kontonummer auf, lautet die Antwort „Konto existiert nicht“; mit jener anderen Nummer kommt ein gültiges Konto zurück. Das erwartete Verhalten wird zu einer Menge von Tests, und diese Tests werden zum Contract.
Von da an funktioniert der Ansatz wie testgetriebene Entwicklung über eine Teamgrenze hinweg. Der Entwickler des Providers muss die Contract-Tests gegen seine API-Implementierung laufen lassen, bevor integriert wird, als Teil der CI, vor Build und Deployment. Bestehen sie, ist eines belegt: Der Provider hat nicht kaputtgemacht, was der Consumer von der Schnittstelle erwartet.
Für die Bank löst das ein Problem, das über Jahre Schmerzen bereitet hat. Teams änderten Schnittstellen, deployten in die Abnahmetestumgebung, in der alle anderen gerade testen wollten, und zehn Systeme standen still. Mit den Contract-Tests in der Pipeline kommt deutlich weniger kaputte Integration überhaupt bis in diese gemeinsame Umgebung.
Ein Mock aus dem Contract verhält sich wie der echte Provider
Wer isoliert testet, braucht Mocks, und genau dort haben Unit-Tests und isolierte Systemtests ihre schwache Stelle. Der Mock beruht auf der Vorstellung des Programmierers, wie sich die andere Komponente verhalten wird. Er ist nicht echt, er ist Vorstellung, und Tests gegen eine Vorstellung sind nicht verlässlich. Phil sieht dasselbe Symptom überall: Entwickler melden 100 Prozent Code Coverage, und der Code ist trotzdem schlecht.
„Wenn Entwickler sagen, sie hätten 100 Prozent Code Coverage, und der Code trotzdem furchtbar ist, dann stimmt da was nicht.“
(Phil Royston)
Die Formen, die die klassische Testpyramide abgelöst haben, der Weihnachtsbaum, die Honeycomb, die Trophy von Spotify und anderen, zeigen alle in dieselbe Richtung: Unit-Tests allein sind wenig effektiv, also testet die Schnittstellen, denn dort sitzen die Fehler. Genau diese Schnittstellen nagelt Contract Testing fest.
Hier zahlt sich der Contract ein zweites Mal aus. Aus den vereinbarten Tests lässt sich ein Mock oder Simulator generieren, der sich exakt so verhält, wie die Tests es beschreiben. Das Consumer-Team entwickelt gegen diesen Mock und bekommt realistische Ergebnisse, vielleicht nicht die endgültigen, aber viel näher an der Wirklichkeit als alles, was es heute hat. Der Provider testet seine API auf dem eigenen Rechner. Der Tester testet in der Entwicklungsumgebung gegen den Mock und deckt dort schon einen guten Teil der Funktionalität ab. Das Testen ist von der Integrationsumgebung entkoppelt, und die Abnahmeumgebung hört auf, der Ort zu sein, an dem alles zum ersten Mal bricht.
Phils Ziel für die nächsten ein bis zwei Jahre ist, diesen entkoppelten Systemtest in Aktion zu sehen: ein System unter Test, bei dem jeder Nachbar weggemockt ist, gebaut aus Contracts, die vor Beginn der Entwicklung vereinbart wurden. In sechs Monaten sieht er das nicht. Die Organisation ist groß, ändert sich langsam, und jeder Schritt braucht viele Gespräche.
Wie gewinnt man eine Bank, die seit 20 Jahren integriert testet?
Phils erster Schritt, nur halb im Scherz: wissen, wie man eine PowerPoint-Folie schreibt. Das Senior Management einer Bank trägt einen KPI, nämlich öfter zu releasen, und sucht nach einer Lösung. Bevor du eine anbietest, sprich mit diesen Leuten. Finde heraus, was ihnen nachts den Schlaf raubt, welche Produktionsvorfälle wehtun und was diese Vorfälle mit ihrer Arbeit machen. Diese Leute arbeiten auf einer hohen Ebene, weil ihr Tag nichts anderes zulässt; 20 Folien zu den technischen Details von Contract-Driven Testing sitzen sie nicht durch. Was sie brauchen, ist eine Botschaft: Hier ist Contract Testing, das können wir damit, und die Testphase in jedem Release-Zyklus sollte kürzer werden. Die Antwort lautete: genehmigt, legt los.
Danach kam der schwierigere Teil. Das Senior Management zu überzeugen ist oft noch der leichte Part; alle anderen zu überzeugen ist die Arbeit. Phil beschreibt sich selbst als den High-Level-Typ, der die Theorie versteht, verkauft und erklärt, und er sagt offen, dass er den nächsten Schritt nicht selbst gehen kann. Der gehörte ein paar starken Technikern, die in die Details gingen, Code-Snippets zeigten und Demos laufen ließen. Sobald die Ingenieure im Raum eine Demo gesehen hatten, waren sie an Bord.
Zwischen den beiden Ebenen sitzt die Gruppe mit der größten Macht zum Veto: die Middle Manager, weil sie die Budgets verantworten. Sie brauchen weder eine Strategiefolie noch einen Code-Snippet. Deshalb bringt Phil seinen Technikern bei, zuerst Ziel und Konzept zu erklären, denn ihre Gewohnheit ist, sofort in den Code zu springen. In einer großen Organisation entscheidet die Kommunikation auf allen drei Ebenen mehr als das Tooling. Und wenn die Veränderung zäh wird und Leute aufgeben wollen, muss jemand sie wieder in den Zug holen.
Freigegebene Business Requirements sind noch keine testbaren Business Requirements
Eine zweite Veränderung lief in der Bank unter dem Namen Business-Driven Development. Ein ranghoher Business-Stakeholder sagte, seine Leute hätten keine Zeit fürs Testen: Projektteams tauchten auf und verlangten sofort einen Benutzerabnahmetest, während das Business gerade mit etwas anderem beschäftigt war. Phil bot Hilfe an, unter einer Bedingung. Die Tester mussten verstehen, was das Business braucht und erwartet, und davon war wenig aufgeschrieben; das meiste steckte in Köpfen. Da die Bank ein neues System einführte und Banken ohnehin Business Requirements schreiben, einigte man sich darauf, genau die zu nehmen.
Dann zeigte sich das Muster. Eine Business-Person erzählt einem Business-Analysten, was sie will. Der Analyst schreibt es genau auf und liest es zurück, die Business-Person bestätigt, denn so hat sie es gesagt. Ein Tester liest dasselbe Dokument und fragt: Warum? Warum willst du das, wofür ist das, und wie finden wir heraus, ob es funktioniert hat? Für Business-Analysten und Solution Architects war das eine schmerzhafte Lernkurve: Eine freigegebene Business-Anforderung ist noch keine testbare. Heute hat die Bank testbare Anforderungen, und die ganze Community of Practice der Business-Analysten trägt den Prozess mit, weil alle ein Argument akzeptiert haben. Man kann von Testern nicht erwarten zu wissen, ob etwas funktioniert, wenn niemand ihnen sagen kann, was es tun soll.
Zwei weitere Veränderungen haben sich daneben festgesetzt. Die Teams nutzen die Testpyramide bewusster und haben viele UI-Tests auf API-Tests verlagert. Für Ende-zu-Ende-Tests, die in einer Bank Prozesse über rund 50 Anwendungen bedeuten, wollte Phil kein weiteres zentrales Team, das Ende-zu-Ende obendrauf testet, während jedes Squad sein eigenes Ding macht. Stattdessen leihen sich die Squads gegenseitig ihre Tests, um die ganze Kette abzudecken; das Team nennt den Ansatz „parasitäres Testen“. Die Testmanager haben ihn übernommen, weil er ihnen Zeit und Energie spart, auch wenn er auf Organisationsebene noch nicht trägt.
Warum braucht eine Testtransformation Messung vom ersten Tag an?
Das Learning, das Phil nach 18 Monaten an erste Stelle setzt, ist das, das er versäumt hat: Er hat nicht angefangen zu messen. Läuft ein Veränderungsprogramm, sind die Leute mit Tun beschäftigt, und nichts davon ist sichtbar. Das muss kein Metrik-Dashboard sein. Es heißt mindestens, regelmäßig zu prüfen, was passiert, was steht und was noch fehlt, und das an die Beteiligten zurückzuspielen.
Was bei ihm inzwischen funktioniert, sind Interviews mit den Testmanagern. Frag sie direkt, was sie für Contract-Tests tun, für API-Tests, für die Anforderungen. Das Interview ist zugleich ein Moment der Kommunikation. Die meisten Testmanager haben vom Programm gehört, wenige haben es verstanden, und fast alle sind zu beschäftigt, um etwas daran zu ändern. Im Gespräch kannst du das Programm mit ihrem konkreten Problem verbinden: Wenn wir das jetzt in deinem Projekt ausprobieren, würde es dir helfen? Zu wenig Kommunikation und zu wenig Feedback sind für Phil die zwei Lücken, die die ersten anderthalb Jahre gebremst haben.
Auf die Krise warten, dann handeln
Nichts in diesem Programm ging schnell, und Phil hat das auch nicht erwartet. Sein Rat an alle in einer ähnlichen Lage: kleine Änderungen machen, Dinge voranbringen, keine Wunder erwarten und auf die Krise warten. Dieselbe Taktik hat er jahrelang als Testmanager gefahren. Er kam in ein Projekt, das Business hatte die Spezifikationen freigegeben, er las sie und sah, dass die niemand ernsthaft freigegeben haben konnte. Das Projekt lief weiter auf seine Meilensteine zu. Allen in diesem Moment zu erklären, dass unterschrieben nicht freigegeben heißt, ändert nichts. Der Moment kommt, wenn die Probleme anfangen, und dann bekommt die Frage „Warum machen wir es nicht so?“ eine Antwort.
In der Bank hat die Krise einen Namen, das Transformationsprogramm, und dass ein Externer herumläuft, der so wirkt, als wüsste er, wovon er redet, gehört zu dem, was die Leute in Bewegung bringt. Manche sind neugierig und wollen dabei sein. Andere sehen das Unbekannte vor sich und wollen jemanden neben sich haben. Beides sind Gründe, geduldig zu bleiben und die Gelegenheit zu nutzen, wenn sie kommt.


