Communitygithub.com

lguidolin/tests-als-kontrollprobe

Verwende dies beim Schreiben oder Ändern von Tests, wenn ein Test während eines Refactorings bricht, oder beim Testen von Berechtigungen/Rollen. Symptome – Versuchung, einen Test anzupassen, damit er grün wird, nur den Happy Path testen, ein plötzlich grüner Deny-Test, flaky Tests oder Unsicherheit darüber, was zu asserten ist.

Was ist tests-als-kontrollprobe?

Diese Skill definiert Tests als „Kontrollproben“ – sie fixieren eine Erwartung, so dass jede Verhaltensänderung sichtbar wird. Die zentrale Regel lautet: *Repariere nicht den Test, um den Code zu bestätigen; verstehe, warum sie unterschiedlicher Meinung sind.* Sie unterscheidet klar zwischen strukturellen Änderungen (Refactoring, Umbenennung interner Symbole) und verhaltensändernden Änderungen (neue Anforderungen, API-Änderungen, Rollen). Strukturelle Änderungen erfordern keine neuen Tests – das gesamte bestehende Testsuite muss unverändert bestehen. Verhaltensänderungen dagegen folgen dem Test-First-Ansatz: Schreiben Sie den fehlschlagenden Test, sehen Sie ihn aus dem richtigen Grund fehlschlagen, und implementieren Sie dann den minimalen Code, der ihn grün macht. Die Skill führt eine detaillierte Tabelle für die Klassifikation von Umbenennungen ein: Interne Namen sind strukturell, öffentliche/exportierte Namen sind verhaltensändernd (auch wenn die Daten gleich bleiben) und erfordern ein Add-and-Deprecate-Muster. Zudem betont sie symmetrische Coverage (Allow und Deny für jede Rolle), das Quarantänisieren von flaky Tests und das Prinzip DAMP (Descriptive And Meaningful Phrases) statt DRY in Tests. Typische Ausreden wie „Ich schreibe zuerst den Code“ oder „Ich aktualisiere einfach den Test beim Refactoring“ werden mit der harten Realität konfrontiert. Die Skill ist plattformunabhängig und richtet sich an jeden Entwickler, der mit Test-First, Refactoring oder Berechtigungslogik arbeitet.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lguidolin/agent-skills/tree/main/skills/tests-as-a-control

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

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 ElementKlassifikationTor
    Internes Symbol, privater Helfer, lokale DateiStrukturellBestehende Suite besteht unverändert
    Exportierte Funktion, API/GraphQL-Feld, Route, CLI-Flag, DB-Spalte, Event-Name, KonfigurationsschlüsselVerhaltensändernd – es bricht NutzerTest-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

AusredeRealitä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.

Individual skills in this repo

This repo contains 18 individual skills — each has its own dedicated page.

lguidolin/change-hygiene-and-code-craft

Use when writing or refactoring code, structuring a commit or PR, or deciding whether to abstract duplication. Symptoms — mixing reorg with logic changes, a PR doing several things at once, a file growing large, the second copy of similar code, or unsure whether to DRY something up.

lguidolin/cloud-delivery-aks

Use when deploying to Kubernetes or Azure Kubernetes Service (AKS), configuring cloud secrets, setting up progressive rollout/canary, per-PR ephemeral environments, or k8s health probes. Keywords — Kubernetes, AKS, Key Vault, Argo Rollouts, Flagger, canary, blue-green, liveness, readiness, PodDisruptionBudget, HPA, rollback, GHCR.

lguidolin/commit-history-rewrite

Use when an existing repository has messy commit history that needs to conform to conventional commits before adopting release-please, or when intermediate WIP/fixup/merge commits need to be cleaned up.

lguidolin/conventional-commits-and-releases

Use when committing, writing a commit message, opening a PR that will be squash-merged, or configuring automated versioning/changelogs. Keywords — conventional commits, release-please, semver, feat/fix/chore, breaking change, changelog.

lguidolin/defense-in-depth-security

Use when handling untrusted input, secrets, authentication/authorization, or dependencies — or threat-modeling a new surface. Keywords — STRIDE, threat model, least privilege, secrets management, supply chain, dependency scanning, input validation, audit log, defense in depth.

lguidolin/designing-before-building

Use when starting a feature, fixing a non-trivial bug, or about to write implementation code — before any code exists. Symptoms you need this: "this is simple, I'll just code it", reaching for the editor before a design is approved, or an idea that hasn't been turned into a spec and plan.

lguidolin/engineering-constitution

Use when starting work in a project that follows the engineering constitution, orienting to its rules, or deciding which engineering practice applies to a task — spec writing, commits, testing, security, deploys, database, or UI work.

lguidolin/graphql-contract-testing

Use when writing a GraphQL query/mutation that the UI and a test will share, or building route/schema contract or smoke tests. Symptoms — copying a query into a test, a test asserting on query text, schema change that didn't break the UI build, or RLS/permission drift. Keywords — graphql-codegen, typed document, contract test, route smoke test.

lguidolin/init-repo-CI

Use when setting up a new repository with conventional commits, release-please, and CI automation, or when retrofitting an existing repository that lacks automated versioning and PR validation workflows.

lguidolin/interface-craft-and-accessibility

Use when building or styling UI — components, layouts, forms, design tokens — or making accessibility decisions. Keywords — a11y, WCAG, keyboard navigation, focus state, contrast, design system, minimalist UI, component reuse, ARIA, semantic HTML.

lguidolin/merge-gates-and-automation

Use when setting up or changing CI, pre-push hooks, or a task runner, or deciding what must pass before merge. Symptoms — tempted to put authoritative checks only in a local hook, skip CI, bypass with --no-verify, or unsure what gates a merge vs. runs locally.

lguidolin/observability-and-slos

Use when adding logging, metrics, tracing, health checks, SLOs, or alerting — or when building a service surface that needs to be operable and debuggable. Keywords — structured logs, OpenTelemetry, correlation id, RED metrics, liveness, readiness, SLI, SLO, error budget, alerting.

lguidolin/performance-and-scale

Use when working on hot paths, list endpoints, pagination, data-access in loops, or public interfaces/schemas. Symptoms — unbounded queries, N+1 access, no latency budget, optimizing without measuring, or changing an interface many consumers depend on. Keywords — pagination, N+1, Hyrum's Law, performance budget, bundle size.

lguidolin/postgres-postgraphile-rls-and-sql

Use when writing PostgreSQL, PostGraphile config, Row-Level Security policies, SQL schema files, or working on the Browser→App→PostGraphile→Postgres data path. Keywords — RLS, SECURITY DEFINER, search_path, pgSettings, grants, roles, GraphQL depth limit, query cost, statement_timeout, SQL file organization.

lguidolin/recording-decisions

Use when a design or architecture decision has been made and needs to be captured — writing a decision record or ADR, updating a decision index, noting a deferred idea, or superseding a past decision. Keywords — ADR, decision record, rationale, rejected alternatives, dependency index.

lguidolin/resilience-and-deploy-safety

Use when planning a deploy, designing a rollback, or responding to an incident or writing a postmortem. Keywords — deploy safety, rollback, immutable artifact, progressive delivery, canary, blast radius, incident response, blameless postmortem, error budget.

lguidolin/ship-it

Use when the user wants to ship work — push, PR, archive decision records, merge, and clean up. Handles the full lifecycle from committing final changes through post-merge cleanup including converting specs/plans to compact decision records.

lguidolin/zero-downtime-migrations

Use when changing a database schema where data must survive the change — adding/removing/renaming columns, constraints, indexes, or backfilling. Symptoms — a destructive migration bundled with a code deploy, a NOT NULL column with a backfill, a table-locking UPDATE, or a rename. Keywords — expand/contract, parallel change, backfill, NOT VALID, CREATE INDEX CONCURRENTLY, graphile-migrate.

Verwandte Skills