11. Acceptance Test Driven Development mit Agenten
Acceptance Test Driven Development mit Agenten
Abschnitt betitelt „Acceptance Test Driven Development mit Agenten“Ein Agent hat ein Problem, das ein Mensch nicht hat: Er weiß nicht, wann er fertig ist. Ein Mensch merkt beim Ausprobieren, dass etwas nicht stimmt, erinnert sich an das Gespräch von gestern, sieht die Oberfläche. Ein Agent sieht nichts davon. Ihm bleibt der eigene Eindruck, und der lautet in praktisch jedem Fall: sieht gut aus.
Deshalb ist die Frage, die dieses Kapitel beantwortet, die zentrale Frage beim agentischen Arbeiten überhaupt: Woran erkennt der Agent, dass er fertig ist? Die Antwort heißt: an einem Kriterium, das eine Maschine prüfen kann und das nicht er selbst formuliert hat.
ATDD in einem Absatz
Abschnitt betitelt „ATDD in einem Absatz“Acceptance Test Driven Development kennen Sie aus dem Kapitel über Teststrategien in Softwareentwicklung. Die Idee: Bevor implementiert wird, wird das gewünschte Verhalten aus Sicht des Anwenders in Beispielen festgehalten, und diese Beispiele werden als ausführbare Tests formuliert. Erst dann beginnt die Implementierung, und sie ist fertig, wenn die Tests grün sind.
Der Unterschied zu testgetriebener Entwicklung im engeren Sinn ist die Ebene. TDD arbeitet an einzelnen Einheiten, ATDD an der Fachlichkeit: nicht „gibt die Funktion validate bei leerem String false zurück”, sondern „ein Projekt ohne Titel kann nicht angelegt werden, und der Benutzer erfährt warum”.
Warum das mit Agenten besonders gut zusammenpasst
Abschnitt betitelt „Warum das mit Agenten besonders gut zusammenpasst“Bei menschlichen Teams ist ATDD eine Disziplinfrage: Man kann auch ohne, es wird nur schlechter. Bei Agenten ist es keine Disziplinfrage, sondern eine technische Voraussetzung, und dafür gibt es drei Gründe.
Der Agent braucht ein maschinelles Signal. Aus Kapitel 8: Ein Agent ist so gut wie seine Rückmeldung. Ein fehlschlagender Test ist die präziseste Rückmeldung, die es gibt, weil sie nicht nur sagt, dass etwas falsch ist, sondern was.
Die Tests schließen die Spezifikationslücke. Ihr Auftrag in natürlicher Sprache ist zwangsläufig unvollständig; das ist die Ursache der Fehlleistung aus Kapitel 10. Ein Test ist unmissverständlich. Wo Prosa mehrdeutig ist, ist ein Testfall eindeutig, und die Mehrdeutigkeit fällt beim Schreiben des Tests auf, nicht nach der Implementierung.
Sie kontrollieren das Ergebnis, ohne jede Zeile zu lesen. Bei zweihundert geänderten Zeilen ist ein zeilenweises Review teuer. Wenn Sie stattdessen die Tests geschrieben oder geprüft haben und diese grün sind, haben Sie eine Aussage über das Verhalten, unabhängig davon, wie der Code aussieht. Das Review verlagert sich vom „ist das richtig” auf „ist das gut gebaut”, und das ist die angenehmere Frage.
Der Ablauf
Abschnitt betitelt „Der Ablauf“flowchart TD
A["User Story<br/>Als ... möchte ich ... damit ..."] --> B["Akzeptanzkriterien<br/>Given / When / Then"]
B --> C["Tests schreiben<br/>Mensch, oder Agent + Review"]
C --> D{"Tests rot?"}
D -->|nein| E["Test prüft nichts<br/>-> korrigieren"]
E --> C
D -->|ja| F["Agent implementiert<br/>Build Mode"]
F --> G{"Tests grün?"}
G -->|nein| F
G -->|ja| H["Refactoring durch den Agenten<br/>Tests bleiben grün"]
H --> I["Review durch den Menschen"]
Zwei Kästchen darin werden regelmäßig übersprungen, und beide zu Unrecht.
„Tests rot?” Ein Test, der vor der Implementierung grün ist, prüft nichts. Das ist der älteste Test-Fehler überhaupt, und er passiert mit Agenten häufiger, weil die Tests schneller entstehen. Der rote Lauf ist der Beweis, dass der Test überhaupt etwas misst.
„Review durch den Menschen” Grüne Tests sagen, dass das gewünschte Verhalten da ist. Sie sagen nichts über Struktur, Lesbarkeit, Sicherheit oder darüber, ob der Code in die Architektur passt. Diese Fragen bleiben beim Menschen (Kapitel 12).
Ein durchgängiges Beispiel
Abschnitt betitelt „Ein durchgängiges Beispiel“Nehmen wir die Aufgabe aus dem Plan in Kapitel 10.
Schritt 1: Akzeptanzkriterien
Abschnitt betitelt „Schritt 1: Akzeptanzkriterien“Aus der User Story werden Beispiele. Die Form Given/When/Then zwingt dazu, Vorbedingung, Aktion und erwartetes Ergebnis zu trennen, und deckt beim Schreiben Lücken auf.
Feature: Create a project
Scenario: A valid project is created Given the project number "P-2026-001" is not taken When a project titled "Website Relaunch" is created with that number Then the service responds with 201 And the response contains an id
Scenario: The title is missing When a project without a title is created Then the service responds with 400 And the response marks the field "title" as invalid
Scenario: The project number has the wrong format When a project is created with the number "2026-1" Then the service responds with 400 And the response marks the field "projectNumber" as invalid
Scenario: The project number is already taken Given a project with the number "P-2026-001" exists When a project is created with the same number Then the service responds with 409Vier Szenarien, ein Gutfall und drei Fehlerfälle. Dieses Verhältnis ist typisch und ein brauchbarer Prüfwert: Wenn Ihre Akzeptanzkriterien überwiegend Gutfälle sind, fehlt etwas.
Schritt 2: Ausführbare Tests
Abschnitt betitelt „Schritt 2: Ausführbare Tests“Aus den Szenarien werden Tests. Ob dazwischen ein Gherkin-Werkzeug steht oder ob direkt Testcode geschrieben wird, ist eine Werkzeugfrage; entscheidend ist, dass jedes Szenario genau einen Test hat.
import { describe, it, expect, beforeEach } from "vitest";import { POST } from "@/app/api/projects/route";import { resetDatabase, seedProject } from "../helpers/db";
const request = (body: unknown) => new Request("http://localhost/api/projects", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), });
describe("POST /api/projects", () => { beforeEach(async () => { await resetDatabase(); });
it("creates a valid project", async () => { const res = await POST(request({ title: "Website Relaunch", projectNumber: "P-2026-001" })); expect(res.status).toBe(201); expect((await res.json()).id).toBeDefined(); });
it("rejects a project without a title", async () => { const res = await POST(request({ projectNumber: "P-2026-001" })); expect(res.status).toBe(400); expect((await res.json()).error.fields).toHaveProperty("title"); });
it("rejects a project number with the wrong format", async () => { const res = await POST(request({ title: "Test", projectNumber: "2026-1" })); expect(res.status).toBe(400); expect((await res.json()).error.fields).toHaveProperty("projectNumber"); });
it("rejects a project number that is already taken", async () => { await seedProject({ title: "Existing", projectNumber: "P-2026-001" }); const res = await POST(request({ title: "New", projectNumber: "P-2026-001" })); expect(res.status).toBe(409); });});$ bun run test tests/api/projects.post.test.ts
FAIL tests/api/projects.post.test.ts × creates a valid project × rejects a project without a title × rejects a project number with the wrong format × rejects a project number that is already taken
Cannot find module '@/app/api/projects/route'
Tests 4 failedGenau so soll es aussehen. Der Fehler ist der richtige: Die Route gibt es noch nicht. Ein Test, der aus einem anderen Grund fehlschlägt (Tippfehler im Import, kaputte Testdatenbank), prüft ebenfalls nichts, und der rote Lauf ist der Moment, das zu bemerken.
Schritt 3: Der Auftrag an den Agenten
Abschnitt betitelt „Schritt 3: Der Auftrag an den Agenten“Jetzt erst kommt die Implementierung ins Spiel, und der Auftrag ist kurz, weil die Spezifikation in den Tests steht.
Implementiere POST /api/projects, sodass tests/api/projects.post.test.tsgrün wird.
Randbedingungen:- Architektur und Fehlerformat nach AGENTS.md, Datenzugriff nur im Repository- Eingabevalidierung mit Zod- Die Testdatei wird NICHT verändert. Wenn ein Test aus deiner Sicht falsch ist, halte an und melde das, statt ihn anzupassen.- Führe nach jeder Änderung `bun run test` und `bun run typecheck` aus.Der dritte Punkt ist der wichtige. Er wird auch dann aufgeschrieben, wenn er technisch abgesichert ist, weil er den Agenten in die richtige Reaktion lenkt: anhalten und melden. Ein Test, den der Agent für falsch hält, ist übrigens ein wertvoller Befund. In etwa jedem fünften Fall hat er recht, und dann haben Sie einen Denkfehler in der Spezifikation gefunden, bevor er im Code steckt.
Die Regel technisch durchsetzen
Abschnitt betitelt „Die Regel technisch durchsetzen“Eine Bitte im Prompt ist eine Bitte. Wer die Regel verlässlich haben will, setzt sie im Harness durch. Drei Ebenen, aufsteigend nach Verlässlichkeit.
-
Im Prompt und in der Regeldatei. Die schwächste Stufe, aber sie kostet nichts und wirkt in den meisten Fällen. In
AGENTS.md: „Dateien untertests/sind für dich schreibgeschützt.” -
Über die Werkzeugrechte. Viele Agenten erlauben Ausschlussmuster für Schreibzugriffe oder eine Bestätigungspflicht für bestimmte Pfade. Damit wird aus der Bitte eine Rückfrage, die Sie sehen.
-
Im Prozess. Die verlässlichste Stufe und die einzige, die auch bei einem Menschen greift: Tests kommen in einen eigenen Commit vor der Implementierung. Damit ist im Merge Request auf einen Blick sichtbar, ob danach noch jemand an ihnen gedreht hat. In der CI lässt sich zusätzlich prüfen, ob ein Merge Request, der Quellcode ändert, gleichzeitig Testdateien ändert, und das zumindest markieren.
Für den Unterricht ist Stufe 3 verbindlich: Der Test-Commit steht vor dem Implementierungs-Commit, und beide sind in der Git-Historie getrennt sichtbar. Das ist gleichzeitig der Nachweis, dass Sie die Reihenfolge eingehalten haben.
Wer schreibt die Tests?
Abschnitt betitelt „Wer schreibt die Tests?“Die naheliegende Frage, und die Antwort ist abgestuft.
Die Akzeptanzkriterien schreiben Sie. Immer. Sie sind die fachliche Aussage, und sie aus dem Auftrag abzuleiten ist genau die Denkarbeit, die niemand abnehmen kann. Ein Agent, der sich die Kriterien selbst ausdenkt, prüft seine eigene Interpretation des Auftrags.
Den Testcode darf ein Agent schreiben, wenn die Kriterien feststehen und Sie den erzeugten Code lesen. Aus Given/When/Then Vitest-Code zu machen, ist Übersetzungsarbeit, und darin sind Agenten gut. Prüfen Sie dabei drei Dinge: Prüft der Test wirklich das Kriterium? Ist er rot, bevor implementiert wird? Prüft er das Verhalten und nicht die Implementierung?
Der dritte Punkt ist der, der am häufigsten schiefgeht. Ein Test, der prüft, dass eine bestimmte interne Funktion aufgerufen wurde, hält jedes Refactoring auf, ohne etwas über die Fachlichkeit auszusagen. Ein Test, der die HTTP-Antwort prüft, überlebt jeden Umbau darunter.
Grenzen des Verfahrens
Abschnitt betitelt „Grenzen des Verfahrens“Nicht alles ist so prüfbar, und es hilft nichts, das zu behaupten.
| Gut testbar | Schlecht testbar | Was stattdessen |
|---|---|---|
| API-Verhalten, Statuscodes | Ob eine Oberfläche schön ist | Entwurfsabnahme durch Menschen |
| Validierung und Fehlerfälle | Ob ein Text verständlich ist | Lesen lassen |
| Geschäftslogik, Berechnungen | Ob der Ablauf sich gut anfühlt | Usability-Test (5. Klasse) |
| Datenzugriff, Migrationen | Ob die Architektur passt | Code-Review |
| Verhalten unter Last | Barrierefreiheit im Detail | Werkzeuge plus manuelle Prüfung |
Für die schwer testbare Spalte gibt es Teilhilfen: Screenshot-Vergleiche für UI-Regressionen, automatisierte Prüfungen für einen Teil der Barrierefreiheitsregeln, Lasttests für das Verhalten unter Druck. Sie ersetzen die menschliche Beurteilung nicht, sie fangen nur die Rückschritte ab.
Und eine Warnung, die auch ohne Agenten gilt: Grüne Tests sind kein Beweis für Korrektheit, sondern ein Beweis dafür, dass die geprüften Fälle stimmen. Ein Agent optimiert genau auf das, was gemessen wird. Wenn Ihre Tests einen Fall nicht abdecken, ist die Wahrscheinlichkeit hoch, dass dieser Fall nicht funktioniert.
Häufige Stolperfallen
Abschnitt betitelt „Häufige Stolperfallen“- Den roten Lauf überspringen. Ein Test, der von Anfang an grün ist, prüft nichts, und niemand merkt es.
- Den Agenten die Tests anpassen lassen. Damit ist das Verfahren wertlos, und zwar vollständig.
- Tests und Implementierung in einem Commit. Dann ist die Reihenfolge nicht mehr nachweisbar.
- Nur Gutfälle testen. Fehlerfälle sind der Ort, an dem Software auseinanderfällt.
- Implementierung statt Verhalten testen. Solche Tests brechen bei jedem Refactoring und sagen fachlich nichts.
- Den Agenten die Akzeptanzkriterien erfinden lassen. Er prüft dann seine eigene Auslegung des Auftrags.
- Grüne Tests für Vollständigkeit halten. Sie sagen etwas über das Geprüfte, sonst nichts.
- Auf das Review verzichten, weil die Tests grün sind. Tests prüfen Verhalten, nicht Struktur und nicht Sicherheit.
Lernergebnisse: Was Sie nach diesem Kapitel können sollten
Abschnitt betitelt „Lernergebnisse: Was Sie nach diesem Kapitel können sollten“- Erklären: begründen, warum ein Agent ein maschinell prüfbares Erfolgskriterium braucht, und dies mit der Rückmeldeschleife aus Kapitel 8 verbinden.
- Anwenden: aus einer User Story Akzeptanzkriterien in der Form Given/When/Then ableiten, mit Gut- und Fehlerfällen.
- Anwenden: Akzeptanzkriterien in ausführbare Tests überführen und den roten Lauf als Nachweis führen.
- Anwenden: einen Auftrag an einen Agenten so formulieren, dass die Tests die Spezifikation sind.
- Erklären: erklären, warum der Agent die Tests nicht verändern darf, und drei Ebenen der Durchsetzung benennen.
- Anwenden: Tests und Implementierung in getrennten Commits ablegen und die Reihenfolge in der Git-Historie nachweisen.
- Beurteilen: beurteilen, ob ein Test das Verhalten oder die Implementierung prüft, und ihn gegebenenfalls umformulieren.
- Beurteilen: die Grenzen des Verfahrens benennen und für schwer testbare Anforderungen ein geeignetes Prüfverfahren wählen.