Was macht KI-Regressionstests?
Testmuster, die speziell für die KI-gestützte Entwicklung entwickelt wurden, wenn dasselbe Modell Code schreibt und überprüft – und dabei systematische blinde Flecken erzeugt, die nur automatisierte Tests aufdecken können.
Wann aktivieren
- Ein KI-Agent (Claude Code, Cursor, Codex) hat API-Routen oder Backend-Logik geändert
- Ein Fehler wurde gefunden und behoben – es muss verhindert werden, dass er erneut auftritt
- Das Projekt hat einen Sandbox-/Mock-Modus, der für datenbankfreie Tests genutzt werden kann
- Ausführen von
/bug-checkoder ähnlichen Überprüfungsbefehlen nach Codeänderungen - Mehrere Codepfade existieren (Sandbox vs. Produktion, Feature-Flags usw.)
Das Kernproblem
Wenn eine KI Code schreibt und dann ihre eigene Arbeit überprüft, trägt sie dieselben Annahmen in beide Schritte. Dies erzeugt ein vorhersagbares Fehlermuster:
KI schreibt Fix → KI überprüft Fix → KI sagt „sieht korrekt aus“ → Fehler besteht weiterhin
Beispiel aus der Praxis (beobachtet in der Produktion):
Fix 1: notification_settings zur API-Antwort hinzugefügt
→ Vergessen, es zur SELECT-Abfrage hinzuzufügen
→ KI hat es überprüft und übersehen (gleicher blinder Fleck)
Fix 2: Es zur SELECT-Abfrage hinzugefügt
→ TypeScript-Build-Fehler (Spalte nicht in generierten Typen)
→ KI hat Fix 1 überprüft, aber das SELECT-Problem nicht erkannt
Fix 3: Auf SELECT * geändert
→ Produktionspfad behoben, Sandbox-Pfad vergessen
→ KI hat es erneut überprüft und übersehen (4. Vorkommen)
Fix 4: Test hat es beim ersten Durchlauf sofort erkannt PASS:
Das Muster: Inkonsistenz Sandbox-/Produktionspfad ist die häufigste KI-bedingte Regression.
Sandbox-Modus-API-Tests
Die meisten Projekte mit KI-freundlicher Architektur haben einen Sandbox-/Mock-Modus. Dies ist der Schlüssel zu schnellen, datenbankfreien API-Tests.
Einrichtung (Vitest + Next.js App Router)
// vitest.config.ts
import { defineConfig } from "vitest/config";
import path from "path";
export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["__tests__/**/*.test.ts"],
setupFiles: ["__tests__/setup.ts"],
},
resolve: {
alias: {
"@": path.resolve(__dirname, "."),
},
},
});
// __tests__/setup.ts
// Sandbox-Modus erzwingen – keine Datenbank nötig
process.env.SANDBOX_MODE = "true";
process.env.NEXT_PUBLIC_SUPABASE_URL = "";
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY = "";
Test-Helfer für Next.js-API-Routen
// __tests__/helpers.ts
import { NextRequest } from "next/server";
export function createTestRequest(
url: string,
options?: {
method?: string;
body?: Record<string, unknown>;
headers?: Record<string, string>;
sandboxUserId?: string;
},
): NextRequest {
const { method = "GET", body, headers = {}, sandboxUserId } = options || {};
const fullUrl = url.startsWith("http") ? url : `http://localhost:3000${url}`;
const reqHeaders: Record<string, string> = { ...headers };
if (sandboxUserId) {
reqHeaders["x-sandbox-user-id"] = sandboxUserId;
}
const init: { method: string; headers: Record<string, string>; body?: string } = {
method,
headers: reqHeaders,
};
if (body) {
init.body = JSON.stringify(body);
reqHeaders["content-type"] = "application/json";
}
return new NextRequest(fullUrl, init);
}
export async function parseResponse(response: Response) {
const json = await response.json();
return { status: response.status, json };
}
Regressionstests schreiben
Das Hauptprinzip: Schreibe Tests für Fehler, die gefunden wurden, nicht für Code, der funktioniert.
// __tests__/api/user/profile.test.ts
import { describe, it, expect } from "vitest";
import { createTestRequest, parseResponse } from "../../helpers";
import { GET, PATCH } from "@/app/api/user/profile/route";
// Definiere den Vertrag – welche Felder MÜSSEN in der Antwort sein
const REQUIRED_FIELDS = [
"id",
"email",
"full_name",
"phone",
"role",
"created_at",
"avatar_url",
"notification_settings", // ← Hinzugefügt, nachdem ein Fehler gefunden wurde, der es vermisst hat
];
describe("GET /api/user/profile", () => {
it("gibt alle erforderlichen Felder zurück", async () => {
const req = createTestRequest("/api/user/profile");
const res = await GET(req);
const { status, json } = await parseResponse(res);
expect(status).toBe(200);
for (const field of REQUIRED_FIELDS) {
expect(json.data).toHaveProperty(field);
}
});
// Regressionstest – dieser genaue Fehler wurde 4 Mal von der KI eingeführt
it("notification_settings ist nicht undefined (BUG-R1-Regression)", async () => {
const req = createTestRequest("/api/user/profile");
const res = await GET(req);
const { json } = await parseResponse(res);
expect("notification_settings" in json.data).toBe(true);
const ns = json.data.notification_settings;
expect(ns === null || typeof ns === "object").toBe(true);
});
});
Sandbox-/Produktions-Parität testen
Die häufigste KI-Regression: Behebung des Produktionspfads, aber Vergessen des Sandbox-Pfads (oder umgekehrt).
// Test, dass Sandbox-Antworten dem erwarteten Vertrag entsprechen
describe("GET /api/user/messages (Konversationsliste)", () => {
it("enthält partner_name im Sandbox-Modus", async () => {
const req = createTestRequest("/api/user/messages", {
sandboxUserId: "user-001",
});
const res = await GET(req);
const { json } = await parseResponse(res);
// Dies hat einen Fehler abgefangen, bei dem partner_name
// zum Produktionspfad hinzugefügt wurde, aber nicht zum Sandbox-Pfad
if (json.data.length > 0) {
for (const conv of json.data) {
expect("partner_name" in conv).toBe(true);
}
}
});
});
Integration von Tests in den Bug-Check-Workflow
Definition eines benutzerdefinierten Befehls
<!-- .claude/commands/bug-check.md -->
# Fehlerüberprüfung
## Schritt 1: Automatisierte Tests (obligatorisch, kann nicht übersprungen werden)
Führe diese Befehle ZUERST vor jeder Code-Überprüfung aus:
npm run test # Vitest-Testsuite
npm run build # TypeScript-Typüberprüfung + Build
- Wenn Tests fehlschlagen → melde als Fehler mit höchster Priorität
- Wenn Build fehlschlägt → melde Typfehler mit höchster Priorität
- Fahre nur mit Schritt 2 fort, wenn beide bestanden wurden
## Schritt 2: Code-Überprüfung (KI-Überprüfung)
1. Konsistenz Sandbox-/Produktionspfad
2. API-Antwortstruktur entspricht den Erwartungen des Frontends
3. Vollständigkeit der SELECT-Klausel
4. Fehlerbehandlung mit Rollback
5. Race-Conditions bei optimistischen Aktualisierungen
## Schritt 3: Für jeden behobenen Fehler einen Regressionstest vorschlagen
Der Workflow
Benutzer: „バグチェックして" (oder "/bug-check")
│
├─ Schritt 1: npm run test
│ ├─ FEHLGESCHLAGEN → Fehler mechanisch gefunden (kein KI-Urteil nötig)
│ └─ BESTANDEN → Weiter
│
├─ Schritt 2: npm run build
│ ├─ FEHLGESCHLAGEN → Typfehler mechanisch gefunden
│ └─ BESTANDEN → Weiter
│
├─ Schritt 3: KI-Code-Überprüfung (mit bekannten blinden Flecken im Hinterkopf)
│ └─ Ergebnisse gemeldet
│
└─ Schritt 4: Für jede Behebung einen Regressionstest schreiben
└─ Nächste Fehlerüberprüfung erkennt, ob die Behebung bricht
Häufige KI-Regressionsmuster
Muster 1: Sandbox-/Produktionspfad-Inkonsistenz
Häufigkeit: Am häufigsten (bei 3 von 4 Regressionen beobachtet)
// FEHLGESCHLAGEN: KI fügt Feld nur zum Produktionspfad hinzu
if (isSandboxMode()) {
return { data: { id, email, name } }; // Neues Feld fehlt
}
// Produktionspfad
return { data: { id, email, name, notification_settings } };
// BESTANDEN: Beide Pfade müssen dieselbe Struktur zurückgeben
if (isSandboxMode()) {
return { data: { id, email, name, notification_settings: null } };
}
return { data: { id, email, name, notification_settings } };
Test, um es zu erkennen:
it("Sandbox und Produktion geben dieselben Felder zurück", async () => {
// In der Testumgebung ist der Sandbox-Modus erzwungen
const res = await GET(createTestRequest("/api/user/profile"));
const { json } = await parseResponse(res);
for (const field of REQUIRED_FIELDS) {
expect(json.data).toHaveProperty(field);
}
});
Muster 2: Fehlende SELECT-Klausel
Häufigkeit: Häufig bei Supabase/Prisma, wenn neue Spalten hinzugefügt werden
// FEHLGESCHLAGEN: Neue Spalte zur Antwort hinzugefügt, aber nicht zu SELECT
const { data } = await supabase
.from("users")
.select("id, email, name") // notification_settings nicht hier
.single();
return { data: { ...data, notification_settings: data.notification_settings } };
// → notification_settings ist immer undefined
// BESTANDEN: SELECT * oder neue Spalten explizit einschließen
const { data } = await supabase
.from("users")
.select("*")
.single();
Muster 3: Fehlerzustandsleckage
Häufigkeit: Mittel – beim Hinzufügen von Fehlerbehandlung zu bestehenden Komponenten
// FEHLGESCHLAGEN: Fehlerzustand gesetzt, alte Daten aber nicht gelöscht
catch (err) {
setError("Laden fehlgeschlagen");
// Reservierungen zeigen immer noch Daten vom vorherigen Tab!
}
// BESTANDEN: Zugehörigen Zustand bei Fehler löschen
catch (err) {
setReservations([]); // Veraltete Daten löschen
setError("Laden fehlgeschlagen");
}
Muster 4: Optimistische Aktualisierung ohne ordnungsgemäßes Rollback
// FEHLGESCHLAGEN: Kein Rollback bei Fehler
const handleRemove = async (id: string) => {
setItems(prev => prev.filter(i => i.id !== id));
await fetch(`/api/items/${id}`, { method: "DELETE" });
// Wenn die API fehlschlägt, ist das Element aus der UI verschwunden, aber noch in der DB
};
// BESTANDEN: Vorherigen Zustand erfassen und bei Fehler zurücksetzen
const handleRemove = async (id: string) => {
const prevItems = [...items];
setItems(prev => prev.filter(i => i.id !== id));
try {
const res = await fetch(`/api/items/${id}`, { method: "DELETE" });
if (!res.ok) throw new Error("API-Fehler");
} catch {
setItems(prevItems); // Rollback
alert("Löschen fehlgeschlagen");
}
};
Strategie: Teste dort, wo Fehler gefunden wurden
Strebe nicht nach 100% Abdeckung. Stattdessen:
Fehler gefunden in /api/user/profile → Schreibe Test für Profil-API
Fehler gefunden in /api/user/messages → Schreibe Test für Nachrichten-API
Fehler gefunden in /api/user/favorites → Schreibe Test für Favoriten-API
Kein Fehler in /api/user/notifications → Schreibe (noch) keinen Test
Warum dies bei KI-Entwicklung funktioniert:
- Die KI neigt dazu, dieselbe Art von Fehler wiederholt zu machen
- Fehler häufen sich in komplexen Bereichen (Authentifizierung, Multi-Pfad-Logik, Zustandsverwaltung)
- Einmal getestet, kann diese exakte Regression nicht mehr auftreten
- Die Testanzahl wächst organisch mit Fehlerbehebungen – keine verschwendete Mühe
Kurzreferenz
| KI-Regressionsmuster | Teststrategie | Priorität |
|---|---|---|
| Sandbox-/Produktions-Inkonsistenz | Gleiche Antwortstruktur im Sandbox-Modus bestätigen | Hoch |
| Fehlende SELECT-Klausel | Alle erforderlichen Felder in der Antwort bestätigen | Hoch |
| Fehlerzustandsleckage | Zustandsbereinigung bei Fehler bestätigen | Mittel |
| Fehlendes Rollback | Zustandswiederherstellung bei API-Fehler bestätigen | Mittel |
| Typ-Cast maskiert null | Bestätigen, dass Feld nicht undefined ist | Mittel |
TUN / NICHT TUN
TUN:
- Schreibe Tests sofort nach dem Finden eines Fehlers (wenn möglich vor der Behebung)
- Teste die API-Antwortstruktur, nicht die Implementierung
- Führe Tests als ersten Schritt jeder Fehlerüberprüfung aus
- Halte Tests schnell (< 1 Sekunde gesamt im Sandbox-Modus)
- Benenne Tests nach dem Fehler, den sie verhindern (z. B. „BUG-R1-Regression")
NICHT TUN:
- Schreibe Tests für Code, der noch nie einen Fehler hatte
- Vertraue auf KI-Selbstüberprüfung als Ersatz für automatisierte Tests
- Überspringe Sandbox-Pfad-Tests mit der Begründung „es sind nur Mock-Daten"
- Schreibe Integrationstests, wenn Komponententests ausreichen
- Strebe nach Abdeckungsprozent – strebe nach Verhinderung von Regressionen an