Tests als Kontrollprobe
Überblick
Ein Test ist eine Kontrollprobe: Er fixiert eine Erwartung, so dass jede Verhaltensänderung sichtbar wird. Die Disziplin lautet: Repariere nicht den Test, um den Code zu bestätigen; verstehe, warum sie unterschiedlicher Meinung sind.
Die Regeln
-
Test-First für verhaltensändernde Änderungen. Schreibe den fehlschlagenden Test, beobachte, wie er aus dem richtigen Grund fehlschlägt, und schreibe dann den minimalen Code, der ihn grün macht. Hast du ihn nicht fehlschlagen sehen, weißt du nicht, dass er testet, was du denkst. Der Schleifenmechanismus selbst ist
superpowers:test-driven-development; diese Skill legt fest, was der Test enthalten muss und was ein Fehlschlag bedeutet, sobald er existiert. Die Regel gilt unabhängig davon, ob diese Skill installiert ist. -
Strukturelle Änderungen werden durch die unveränderte Suite verifiziert. Eine rein strukturelle Änderung – umbenennen, verschieben, aufteilen, extrahieren, ohne beobachtbare Verhaltensänderung – benötigt keinen neuen Test. Ihr Tor ist das Gegenteil: Die gesamte bestehende Suite besteht, ohne dass ein Test bearbeitet wurde. Deshalb gilt Test-First hier nicht – es gibt kein Verhalten zu spezifizieren, also kann kein Test sinnvoll zuerst fehlschlagen. Erzwingt eine strukturelle Änderung eine Testbearbeitung, war sie nicht strukturell. Stoppe und klassifiziere anhand der nächsten Regel neu.
-
Eine Umbenennung, die eine Vertragsgrenze überschreitet, ist kein Refactoring. Die Frage ist nicht „Hat der Code das Verhalten geändert?“, sondern „Ist dieser Name für etwas beobachtbar, das ich nicht kontrolliere?“
Umbenanntes Element Klassifikation Tor Internes Symbol, privater Helfer, lokale Datei Strukturell Bestehende Suite besteht unverändert Exportierte Funktion, API/GraphQL-Feld, Route, CLI-Flag, DB-Spalte, Event-Name, Konfigurationsschlüssel Verhaltensändernd – es bricht Nutzer Test-First + hinzufügen und auslaufen lassen Eine öffentliche Umbenennung macht mit denselben Daten dasselbe und ist dennoch eine brechende Änderung, weil der Name der Vertrag war (Hyrums Gesetz – siehe
performance-and-scale). Behandle sie als verhaltensändernd: füge den neuen Namen mit einem Test-First-Zyklus hinzu, behalte den alten Namen und seinen bestehenden Test während der Auslaufphase am Leben, entferne den alten erst, wenn die Nutzer migriert sind.Der dritte Fall ist ein Test, der einen Namen als String referenziert – Snapshots, DI-Container-Schlüssel, Reflection, Fixture-Pfade. Diese brechen bei einer internen Umbenennung. Das ist kein Fehlschlagen der Regel; es ist das Signal, dass der Test an Implementierungsdetails gekoppelt war. Behebe die Kopplung, nicht die Referenz.
-
Refactorings dürfen keine Tests bearbeiten; Anforderungsänderungen müssen es. Unterschiedliche Handlungen:
- Ein Refactoring bricht einen Test → der Test hat eine unbeabsichtigte Verhaltensänderung erkannt. Stoppe und verstehe sie. Den Test zu bearbeiten, um ein Refactoring grün zu machen, zerstört deine Instrumentierung.
- Eine Anforderung hat sich tatsächlich geändert → aktualisiere den Test bewusst, als eigene überprüfbare Änderung, dokumentiert als solche.
- Der verbotene Schritt ist das stille Neujustieren der Referenz.
-
Symmetrische Coverage – jede Regel in beide Richtungen getestet. Beweise, dass der autorisierte Akteur kann und der nicht autorisierte Akteur nicht kann. Reine Happy-Path-Tests prüfen nur die Hälfte einer Regel.
-
Berechtigungs-/Rollenregeln sind eine Matrix: jede sinnvolle (Rolle × Aktion × Ressource) Zelle, Allow und Deny. Eine neue Rolle ist unvollständig, bis ihre Deny-Fälle existieren. Ein Deny-Test, der plötzlich grün wird, ist eine Sicherheitsregression – überprüfe und akzeptiere explizit, richte niemals neu aus.
-
Einstiegspunkte werden niemals still hinzugefügt/entfernt. Routen, Befehle, Endpunkte erhalten jeweils Existenz-und-Rauch-Coverage, so dass Hinzufügen/Entfernen eine Teständerung erzwingt.
-
Test nach Größe: klein (reine Logik, keine E/A, konstant), mittel (echte DB/Prozess, hermetisch), groß (Full Stack). Viele kleine, weniger mittlere, am wenigsten große. Beachte die fehlende Mitte – Server/App-Logik, die weder Unit- noch Integrationstests abdecken, weil jede Schicht annimmt, die andere teste sie.
-
Flaky Tests werden auf Sicht unter Quarantäne gestellt. Ein Test, der ohne Codeänderung besteht/fehlschlägt, zerstört das Vertrauen in die gesamte Suite. Sofort unter Quarantäne (aus dem blockierenden Tor, ein Ticket zur Behebung) – niemals intermittierend fehlschlagen lassen, niemals still löschen. Kleine Tests müssen deterministisch sein: keine echte Uhr, kein Zufall, kein Netzwerk.
-
Testcode ist DAMP, nicht DRY. Descriptive And Meaningful Phrases vor Deduplizierung. Ein Test muss im Alleingang offensichtlich lesbar sein. DRY auf Tests anzuwenden ist ein Verstoß, keine Tugend (siehe
change-hygiene-and-code-craft).
Häufige Rechtfertigungen
| Ausrede | Realität |
|---|---|
| „Ich schreibe zuerst den Code, dann füge ich Tests hinzu“ | Dann wurde der Test an den Code angepasst, nicht an die Anforderung. Du hast ihn nie fehlschlagen sehen, also weißt du nicht, ob er irgendetwas testet. |
| „Es ist nur eine Umbenennung, kein Test nötig“ | Richtig – wenn der Name intern ist. Wenn er exportiert/öffentlich ist, war der Name der Vertrag und die Umbenennung ist verhaltensändernd. |
| „Ich habe es umbenannt und musste drei Tests korrigieren, das ist normal“ | Ist es nicht. Entweder hast du eine Vertragsgrenze überschritten, oder diese Tests waren an Implementierungsdetails gekoppelt. Klassifiziere neu. |
| „Der Test ist jetzt falsch, ich aktualisiere ihn einfach“ | Nur, wenn sich eine Anforderung geändert hat. Wenn du refaktorierst, hat der Test eine echte Änderung erkannt – untersuche sie. |
| „Ich habe den Happy Path getestet, das reicht“ | Eine Regel ist erst getestet, wenn auch der Deny-Fall getestet ist. |
| „Dieser Deny-Test wird jetzt grün, super, ausliefern“ | Eine verbotene Aktion, die erfolgreich ist, ist eine Sicherheitsregression, kein Fortschritt. |
| „Der Test ist flaky, einfach CI neu starten“ | Flakiness verdeckt echte Bugs und trainiert alle, Fehler zu ignorieren. Quarantäne und beheben. |
| „Lass mich diese Tests mit einem gemeinsamen Helfer DRYen“ | Tests sind DAMP. Duplizierung, die die Klarheit vor Ort fördert, bleibt. |
Rote Flaggen – STOPP
- Produktionscode für eine Verhaltensänderung schreiben, bevor ein fehlschlagender Test existiert
- Eine strukturelle Änderung, deren Diff auch Testdateien berührt
- Umbenennung eines öffentlichen/exportierten Namens an Ort und Stelle statt hinzufügen und auslaufen lassen
- Bearbeiten eines Tests während eines Refactorings, um ihn grün zu bekommen
- Eine neue Berechtigung ohne Deny-Fall-Test
- Neujustieren eines Snapshots/Assertions, ohne zu verstehen, warum er sich geändert hat
- Ein Test mit echtem
sleep,Date.now(), zufälligen Daten oder Netzwerkaufruf - Refactoring von Testcode, um Duplizierung auf Kosten der Lesbarkeit zu entfernen
Vollständige Begründung: Artikel IV der Verfassung, gebündelt in engineering-constitution/references/engineering-constitution.md. Für die GraphQL/Route-Vertragsdetails siehe graphql-contract-testing.