Engineering
11 Min.

Testen, wenn Maschinen den Code schreiben

Wenn dasselbe Modell den Code und die Tests schreibt, prüfen die Tests nichts mehr – sie kodieren dasselbe Missverständnis ein zweites Mal. Ein grüner Balken beweist dann nur noch, dass der Code mit sich selbst einverstanden ist. Warum Testen im KI-Zeitalter neu gedacht werden muss: als die vom Menschen verantwortete Spezifikation korrekten Verhaltens.

Der grüne Balken, der nichts beweist

Es gibt einen Moment, den jedes Team kennt und niemand hinterfragt: Die Tests laufen durch, der Balken wird grün, und alle atmen aus. Grün heißt fertig. Grün heißt sicher. Grün heißt, jemand hat nachgedacht und das System hat zugestimmt. Diese kleine Farbe trägt eine erstaunliche Menge an Vertrauen – und dieses Vertrauen beruht auf einer stillen Annahme, die jahrzehntelang gehalten hat: dass die Person, die den Test geschrieben hat, und die Person, die den Code geschrieben hat, unabhängig zum selben Ergebnis gekommen sind. Zwei Wege, ein Ziel. Wenn sie sich in der Mitte treffen, ist das ein Beweis.

Genau diese Annahme bricht, wenn eine Maschine beides schreibt. Das Modell produziert die Funktion, und im selben Atemzug produziert es die Tests, die diese Funktion prüfen. Beide entstehen aus demselben Verständnis der Aufgabe, aus derselben Interpretation der Anforderung, aus denselben blinden Flecken. Der Test prüft nicht mehr, ob der Code richtig ist. Er prüft, ob der Code das tut, was das Modell dachte, dass er tun sollte – und das ist eine völlig andere, viel schwächere Aussage.

Die zirkuläre Validierungs-Falle

Stellen wir uns den einfachsten Fall vor. Ein Modell soll eine Funktion schreiben, die einen Rabatt berechnet. Es liest die Anforderung, versteht sie leicht falsch – sagen wir, es wendet den Rabatt vor der Steuer an, obwohl er danach kommen müsste – und schreibt sauberen, plausiblen Code, der genau diesen Fehler enthält. Dann bitten wir dasselbe Modell, Tests zu schreiben. Es schreibt sie gegen sein eigenes Verständnis. Der Test erwartet den Rabatt vor der Steuer. Der Code liefert den Rabatt vor der Steuer. Grün.

Der Test hat funktioniert – im Sinne von: er ist durchgelaufen. Aber er hat nichts geprüft. Er hat den Fehler nicht gefunden, weil er aus derselben Quelle stammt wie der Fehler. Er hat das Missverständnis nicht entlarvt, sondern es eingefroren, versiegelt und mit einem grünen Häkchen adelt. Das ist die zirkuläre Validierungs-Falle: Wenn dieselbe Instanz die Behauptung und den Beweis der Behauptung liefert, ist der Beweis wertlos. Man kann sich nicht selbst zum Zeugen der eigenen Korrektheit machen.

Der Kern des Problems lässt sich in einem Satz sagen: Grün ist nicht dasselbe wie korrekt. Grün heißt nur, dass zwei Artefakte übereinstimmen. Wenn beide Artefakte von derselben Quelle mit derselben Fehlinterpretation stammen, ist ihre Übereinstimmung keine Neuigkeit. Sie ist eine Tautologie. Man kann auf diese Weise mühelos 100 % Coverage des falschen Verhaltens erreichen – jede Zeile durchlaufen, jeder Zweig getestet, jede Zusicherung erfüllt, und trotzdem berechnet die Software den Rabatt falsch. Die Metrik glänzt, während das Produkt lügt.

Das Tückische daran ist, dass es von außen exakt wie funktionierende Qualitätssicherung aussieht. Dieselbe Test-Suite, dieselbe grüne Pipeline, dieselben Prozentzahlen im Report. Nur dass die Prüfung ihre Unabhängigkeit verloren hat – die eine Eigenschaft, die einen Test überhaupt zu einem Test macht. Ein Test, der nichts prüfen kann, was sein Autor nicht ohnehin schon geglaubt hat, ist kein Sicherheitsnetz. Er ist ein Spiegel.

Tests als ausführbare Spezifikation

Wenn das Modell nicht mehr sein eigener Prüfer sein kann, muss die Unabhängigkeit von woanders kommen. Sie muss vom Menschen kommen. Und der Ort, an dem sie in den Prozess eintritt, ist nicht die Implementierung – die darf die Maschine gern übernehmen –, sondern die Spezifikation: die präzise, verbindliche Aussage darüber, was „korrekt" in diesem Fall bedeutet.

Der Perspektivwechsel ist fundamental. Ein Test hört auf, ein nachträglicher Beweis dafür zu sein, dass der Code funktioniert, und wird zur vorher formulierten Definition dessen, was funktionieren überhaupt heißt. Der Rabatt kommt nach der Steuer. Ein negativer Betrag ist ungültig und muss abgewiesen werden. Ein Warenkorb ohne Artikel ergibt keinen Rabatt, nicht null und nicht einen Fehler, sondern exakt null Euro. Das sind keine Implementierungsdetails. Das sind Geschäftsentscheidungen – und Geschäftsentscheidungen gehören dem Menschen, weil nur der Mensch die Konsequenzen verantwortet.

In der Praxis heißt das nicht zwingend, dass ein Mensch jede Testzeile von Hand tippt. Es heißt, dass ein Mensch den Verhaltens-Vertrag besitzt. Es gibt ein Spektrum, und jeder Punkt darauf ist legitim, solange die Autorenschaft klar bleibt:

  • Der Mensch schreibt die zentralen Zusicherungen selbst – die paar Zeilen, die festhalten, was passieren muss – und lässt die Maschine den Rest der Test-Mechanik drumherum bauen: Fixtures, Mocks, Setup.
  • Der Mensch formuliert den Vertrag in Prosa, präzise und vollständig, und lässt das Modell ihn in Tests übersetzen – prüft die Übersetzung dann aber Zeile für Zeile, so streng, als hätte er sie selbst geschrieben.
  • Der Mensch definiert die Beispiele, die zählen: die konkreten Eingabe-Ausgabe-Paare, an denen sich das Verhalten festmacht. Das Modell darf sie ausformulieren, aber die Paare selbst kommen aus menschlichem Urteil über die Domäne.

Der gemeinsame Nenner ist immer derselbe: Die Definition von korrekt darf nicht aus derselben Quelle stammen wie die Implementierung. Sobald sie es tut, ist die Prüfung zirkulär und der grüne Balken hohl. Die Test-Suite wird dann zu dem, was sie eigentlich immer hätte sein sollen und im menschlichen Zeitalter nur selten war: die ausführbare, unabhängige Spezifikation des Systems. Der Code ist eine Antwort auf sie, nicht ihr Ko-Autor.

Eigenschaften statt Beispiele: Property-based und Invarianten-Tests

Es gibt eine Klasse von Tests, die gegen die zirkuläre Falle strukturell widerstandsfähiger ist – weil sie schwerer zu „gamen" ist, absichtlich wie unabsichtlich. Beispielbasierte Tests sagen: „Bei Eingabe X erwarte ich Ausgabe Y." Ein Modell, das den Code und den Test schreibt, wählt X und Y aus demselben Verständnis und trifft sich selbst. Property-based Tests sagen etwas grundlegend anderes: „Für jede mögliche Eingabe muss diese Eigenschaft gelten."

Das ist die Idee hinter Werkzeugen im Stil von Hypothesis oder QuickCheck: Man beschreibt nicht ein einzelnes Beispiel, sondern eine Invariante – eine Wahrheit über das System, die immer gelten muss, egal welche Daten hereinkommen –, und das Framework wirft Hunderte zufällig erzeugter Eingaben dagegen, gezielt auch die hässlichen: die leere Menge, die riesige Zahl, das Unicode-Chaos, die negative Grenze. Und wenn ein Fall die Invariante bricht, schrumpft es ihn auf das kleinste Gegenbeispiel zusammen und legt es einem vor die Füße.

Warum ist das schwerer zu gamen? Weil eine Invariante auf einer höheren Ebene formuliert wird als die Implementierung. „Der ausgegebene Betrag ist nie negativ." „Etwas ver- und wieder entschlüsseln ergibt das Original." „Die sortierte Liste hat dieselben Elemente wie die unsortierte." „Zweimal dieselbe Operation ausführen ändert nichts gegenüber einmal" – Idempotenz. Solche Sätze fangen ganze Klassen von Fehlern, an die niemand explizit gedacht hat, weil sie nicht ein Beispiel abfragen, sondern eine Wahrheit behaupten. Das Modell kann seinen Code nicht einfach auf die Testfälle hin zuschneidern, weil es die Testfälle gar nicht kennt – sie werden erst zur Laufzeit gewürfelt.

Der Kontrast zum anderen Ende des Spektrums könnte schärfer nicht sein. Snapshot-Tests frieren einfach das aktuelle Ist-Verhalten ein: Beim ersten Lauf nimmt der Test auf, was der Code ausgibt, und ab dann schlägt er an, wenn sich die Ausgabe ändert. Das ist nützlich gegen unbeabsichtigte Regressionen – aber es prüft null Korrektheit. Ein Snapshot eines Fehlers ist ein eingefrorener Fehler, feierlich versiegelt und gegen jede Korrektur verteidigt. Wenn ein Modell den Code und den Snapshot erzeugt, hat man den zirkulären Test in seiner reinsten Form: „Der Code tut, was der Code tut." Snapshots sind ein Werkzeug, kein Beweis, und sie mit Verifikation zu verwechseln ist einer der teuersten Fehler in einer KI-lastigen Codebasis.

Vom Coverage-Prozent zur echten Anforderung

Der wichtigste Wandel ist am Ende kein technischer, sondern einer der bewerteten Frage. Jahrzehntelang lautete die Leitmetrik: Wie viel Prozent des Codes deckt die Test-Suite ab? Diese Zahl war nie besonders aussagekräftig, aber im KI-Zeitalter wird sie geradezu irreführend, denn ein Modell erzeugt mühelos die Tests, die jede Zeile berühren, ohne dass ein einziger davon eine echte Anforderung prüft. Coverage misst, was ausgeführt wurde, nicht, was zugesichert wurde. 100 % einer Lüge ist immer noch eine Lüge.

Die Frage, die zählt, lautet nicht mehr „wie viel deckt der Test ab?", sondern „kodiert dieser Test eine echte Anforderung?". Steht hinter dieser Zusicherung eine Aussage über das Geschäft, über das erwartete Verhalten, über etwas, das wehtäte, wenn es kippte – oder berührt sie bloß eine Zeile, damit die Statistik stimmt? Ein einziger Test, der eine reale Invariante festhält, ist mehr wert als hundert, die Coverage produzieren. Und wenn die Metrik sich ändert, ändert sich, worauf Menschen ihre knappe Aufmerksamkeit richten.

Diese Aufmerksamkeit gehört zuerst an die Ränder, die das Modell überspringt. Generierter Code beschreibt den Glückspfad eloquent und lässt die Kanten aus: die leere Liste, den negativen Betrag, die Zeitzone an der Tagesgrenze, den gleichzeitigen Zugriff, das Timeout mitten in der Transaktion. Das sind genau die Fälle, die kein Beispiel im Trainingsmaterial betont hat – und genau die Fälle, in denen Systeme in Produktion sterben. Der Mensch, der die Domäne kennt, weiß, wo die Leichen liegen. Das Modell weiß es nicht.

Ein wirksames Vorgehen ist, das Modell adversariale Tests gegen seinen eigenen Code generieren zu lassen: „Hier ist die Implementierung – schreibe die fiesesten Testfälle, die du dir vorstellen kannst, um sie zu brechen." Das ist überraschend produktiv, weil ein Modell in der zerstörerischen Rolle oft Kanten sieht, die es in der konstruktiven übersprungen hat. Aber – und das ist die Warnung, die aus der Disziplin des Code Review direkt herüberreicht – es teilt die blinden Flecken. Dasselbe Modell mit derselben Trainingsverzerrung übersieht als Angreifer dieselbe Klasse von Fehlern, die es als Autor eingebaut hat. Es findet die Kanten, die es kennt, und tappt an denen vorbei, die es nie gesehen hat. Adversariale KI-Tests sind eine zusätzliche Schicht, nie die letzte. Die letzte ist ein Mensch mit einer Bedrohungsvorstellung, die kein Modell besitzt.

Und es gibt ein Werkzeug, um die Tests selbst zu prüfen – um zu messen, ob sie überhaupt etwas fangen können. Mutation Testing verändert absichtlich den Produktivcode: dreht ein > in ein >=, streicht eine Zeile, negiert eine Bedingung – und schaut, ob irgendein Test daraufhin rot wird. Bleibt trotz der eingeschmuggelten Mutation alles grün, dann prüft die Suite an dieser Stelle nichts: Der Test läuft durch den Code, ohne sein Verhalten festzunageln. Mutation Testing ist die ehrlichste Antwort auf die Frage „testen unsere Tests eigentlich?" – und in einer Welt, in der Maschinen Tests am Fließband erzeugen, ist es die naheliegende Kontrollinstanz über deren Qualität.

Was KI-Tests trotzdem taugen

Jetzt die ehrliche Gegenseite, sonst kippt das Argument ins Dogma. Nichts davon heißt, dass KI-generierte Tests wertlos sind oder verboten gehören. Das wäre teurer Unsinn.

Als erste Schicht sind sie ausgesprochen nützlich. Sie fangen Regressionen – wenn eine spätere Änderung ein Verhalten bricht, das vorher stimmte, schlägt der generierte Test an, ganz gleich, wer ihn geschrieben hat. Sie erledigen die stumpfe Boilerplate: das Setup, die Fixtures, die zwanzig langweiligen Fälle rund um den einen interessanten. Sie erreichen in Minuten eine Grundabdeckung, für die ein Mensch Stunden bräuchte. Und – das ist der eigentliche Gewinn – sie befreien den Menschen für die bedeutsamen Invarianten. Wer nicht mehr die Fixtures tippt, hat den Kopf frei für die eine Zusicherung, die wirklich zählt. Die Arbeitsteilung ist nicht „Mensch oder Maschine", sondern „Maschine für Menge, Mensch für Bedeutung".

Der Fehler ist nicht, KI-Tests zu benutzen. Der Fehler ist, sie mit Verifikation zu verwechseln – die erste Schicht für die letzte zu halten und aufzuhören, sobald es grün ist. Man verbietet sie nicht; man weigert sich nur, ihnen die Verantwortung zu übergeben, die sie nicht tragen können.

Und noch eine Einschränkung, damit man die Lehre nicht überdehnt: Nicht alles braucht schweres Testen. Die Sorgfalt sollte sich am Sprengradius bemessen – daran, was im schlimmsten Fall passiert, wenn dieser Code falsch ist. Ein Wegwerf-Skript, das einmal eine CSV umformt, braucht keine Invarianten-Suite. Ein internes Dashboard für fünf Leute ist nicht die Zahlungsabwicklung. Die volle Strenge – von Hand besessene Spezifikationen, Property-based Tests, Mutation Testing – gehört dorthin, wo ein Fehler wirklich wehtut: Geld, Kundendaten, Authentifizierung, alles Unumkehrbare. Alles maximal zu prüfen ist seine eigene Form von Verschwendung. Die Kunst ist, die knappe menschliche Aufmerksamkeit dorthin zu lenken, wo Korrektheit nicht verhandelbar ist.

Fazit

Jahrzehntelang war der Test das nachträgliche Häkchen hinter der eigentlichen Arbeit, dem Schreiben des Codes. Diese Reihenfolge kehrt sich um. Wenn die Maschine den Code schreibt, ist der Test nicht mehr der Nachweis, dass es funktioniert hat – er ist die vorher getroffene, vom Menschen verantwortete Definition dessen, was funktionieren überhaupt bedeutet. Die Test-Suite ist die Spezifikation, und die Spezifikation gehört nicht der Maschine, die sie erfüllt.

Genau hier setzen wir bei NH Labs an. Wir lassen KI schreiben, was sich gut schreiben lässt: die Implementierung, die Boilerplate, die zwanzig langweiligen Fälle, die Grundabdeckung in Minuten. Aber den Verhaltens-Vertrag besitzen wir – die Zusicherungen, die festhalten, was „korrekt" in diesem System, mit diesen Daten, für diesen Kunden heißt. Ein Modell kann grün erzeugen, so viel es will; was grün bedeuten darf, definiert ein Mensch. Deshalb dürfen unsere Kunden dem grünen Balken vertrauen: nicht, weil eine Maschine mit sich selbst einverstanden war, sondern weil ein Mensch vorher festgelegt hat, was Übereinstimmung überhaupt beweisen muss.