9.228 Tests. 24 absichtlich übersprungen. 0 Fehler.
Wenn ich das erzähle, kommt meist dieselbe Frage: Warum? Du bist Solo-Entwickler. Das ist ein Nebenprojekt. Niemand bezahlt dich für Tests.
Diese Reaktion versteht falsch, wofür Tests da sind.
Tests sind nicht für QA. Sie sind für Geschwindigkeit.
Die ehrliche Geschichte: ZenAI hat 141 Entwicklungsphasen, entstanden über etwa 12 Monate. In Phase 50 hatte ich ca. 2.000 Tests. In Phase 100 etwa 5.000. In Phase 141 sind es 9.228.
Die konterintuitive Erkenntnis: Je mehr Tests ich hatte, desto schneller konnte ich neue Features liefern.
Nicht langsamer. Schneller.
Mit umfassender Test-Coverage kann man furchtlos refaktorisieren. Man kann ein neues Modul hinzufügen, ohne mental tracken zu müssen, was alles kaputt gehen könnte. Man kann einen 200-Datei-PR mergen und sofort wissen, ob irgendetwas regressiert — nicht durch eine Stunde Rumklicken in der App, sondern in 45 Sekunden.
Das ist kein Overhead. Das ist ein Superkraft.
Die Zusammensetzung
Backend — 7.720 Tests (Jest, TypeScript)
Frontend — 1.400 Tests (Vitest)
CLI — 108 Tests (Jest)
─────────────────────────
Gesamt — 9.228 bestanden
Übersprungen — 24 (alle absichtlich)
Fehlgeschlagen — 0
Die 24 absichtlichen Skips sind dokumentiert:
- 21 Docker-Sandbox-Tests (kein Docker in CI)
- 1 URL-Fetch-Echtanfrage
- 2 SSL-Zertifikat-Umgebungs-Checks
Ich weiß genau, warum jeder einzelne übersprungen wird. Es gibt keine "flaky Tests, die wir auskommentiert haben."
Was getestet wird
Die Backend-Test-Suite deckt 35 Module über 6 Schichten ab:
Integrationstests treffen echte Route-Handler mit gemockten Datenbanken. Sie testen den vollständigen Request/Response-Zyklus — Authentifizierung, Validierung, Business-Logik, Fehlerbehandlung.
Unit-Tests decken einzelne Services ab: den FSRS-Scheduler, Hebbian Dynamics, Knowledge-Graph-Operationen, RAG-Pipeline-Komponenten, Billing-Logik, Memory-Konsolidierung.
Service-Tests mocken externe Abhängigkeiten (Stripe, SendGrid, Anthropic API), üben aber die eigentliche Service-Logik. Der Billing-Service hat 61 Tests für Checkout, Webhooks, Credit-Abzug und Plan-Gating.
Die Frontend-Tests decken 8 React-Query-Hook-Familien, 15+ Komponentenverhalten und 3 komplexe UI-Flows ab (Chat-Streaming, Idea-Management, Settings-Persistenz).
Die Philosophie hinter den Zahlen
Ich folge einer einfachen Regel: Jeder PR, der Code liefert, muss Tests liefern.
Nicht "Tests schreiben, wenn Zeit ist." Nicht "Tests kommen später." Jedes Feature, jede Route, jeder Service. Immer.
Das klingt offensichtlich. Die meisten Entwickler stimmen dem prinzipiell zu. Die wenigsten praktizieren es. Es gibt immer einen Grund: die Deadline, den Prototyp, das "Das refaktorieren wir sowieso noch."
Diese Gründe akkumulieren sich zu einer Codebasis, die man nicht mehr anfassen möchte.
Die konkreten Patterns, die es möglich machten
1. Den Vertrag testen, nicht die Implementierung
Ich teste, was eine Funktion verspricht, nicht wie sie es macht. Wenn getSubscription(userId) ein Subscription-Objekt mit einem plan-Feld zurückgeben soll, das ist der Test. Nicht, dass sie db.query mit einem bestimmten SQL-String aufruft.
Das bedeutet: Tests überleben Refactoring. Als ich von einem ORM zu Raw Queries migriert bin, brach kein einziger Test.
2. An der Grenze mocken, nicht innen
Externe Services (Datenbank, Stripe, Anthropic) werden an der Modulgrenze gemockt. Alles andere läuft echt. Das findet Logikfehler, ohne echte Infrastruktur zu benötigen.
3. Die 5-Test-Regel
Für jede nicht-triviale Route oder jeden Service: Happy Path, fehlende Auth, ungültige Eingabe, Datenbankfehler, Grenzfall. Fünf Tests, zehn Minuten. Die Disziplin, diese fünf immer zu schreiben, fängt 80% der echten Bugs ab.
4. Tests als Dokumentation
Die Testnamen beschreiben, was das System macht. describes('POST /api/:context/tasks').it('creates task with dependency tracking') ist bessere Dokumentation als ein README, das veraltet.
Der Phase-97-Wendepunkt
Etwa in Phase 97 machte ich ein tiefes Qualitäts-Audit: 59 Fixes in 12 Bereichen. Die Route-Coverage stieg in einem Sprint von 38% auf 98%.
Die Erkenntnis war einfach: Nicht getesteter Code ist eine Verbindlichkeit. Kein zukünftiges Problem. Ein gegenwärtiges. Jeder ungetestete Pfad ist ein Verhalten, über das man nicht nachdenken kann, eine Änderung, die man nicht sicher machen kann, ein Bug, den man in Produktion statt im Editor findet.
Nach diesem Audit wurde Test-Coverage die Metrik, die ich am sorgfältigsten verfolgte. Nicht Lines of Code. Nicht gelieferte Features. Bestandene Tests.
Was das ermöglichte
Ein konkretes Beispiel: Der Phase-144-PR fügte Twitter OAuth, LinkedIn-Integration, Governance-Flow, Metrics-Worker und einen BullMQ-Scheduler hinzu — 15 Dateien, 94 Tests — in einer einzigen Session.
Das ist möglich, weil:
- Der umgebende Code hatte 95%+ Coverage
- Das neue Modul konnte hinzugefügt werden, ohne das Interface erraten zu müssen
- Die Tests für den neuen Code wurden parallel zur Implementierung geschrieben
- Die CI-Pipeline fing zwei Integrationsfehler ab, bevor ich den PR überhaupt reviewed hatte
Die Tests haben sich in diesem einzelnen PR amortisiert.
Die ehrlichen Kosten
Tests schreiben braucht Zeit. Im Durchschnitt verbringe ich 30-40% der Implementierungszeit auf Tests.
Für ein finanziertes Team mit Deadlines mag das wie ein Luxus wirken. Für einen Solo-Entwickler, der etwas Dauerhaftes baut, ist es der einzig vernünftige Ansatz.
Die Rechnung: 30% extra Zeit vorab eliminiert mindestens das 5-fache dieser Zeit in Debugging, Regression-Suche und angstgetriebenen Rewrites. Ich habe beobachtet, wie finanzierte Teams mit 10x dem Personal halb so viele Features pro Woche liefern, weil ihre Codebasis fragil geworden ist.
Es gibt keinen Hack um das herum. Entweder man investiert in Tests, oder man zahlt die Zinseszinsen der technischen Schulden.
Das Setup
Für alle, die diesen Ansatz replizieren wollen:
Backend (Jest + TypeScript):
cd backend && npm test # Alle 7.720 Tests
cd backend && npm test -- --testPathPatterns="billing" # Einzelne Suite
cd backend && npm test -- --coverage # Mit Coverage-Report
Frontend (Vitest):
cd frontend && npx vitest run # Alle 1.400 Tests
CI (GitHub Actions): 5 Shards, SKIP_EXTERNAL_SERVICES=true, läuft in ~45 Sekunden.
Der Test-Runner ist das Erste, was ich jeden Morgen öffne. Grün bedeutet, die Arbeit vom Vortag ist solide. Rot bedeutet, ich weiß genau, was ich fixen muss, bevor ich irgendetwas Neues anfange.
Das Fazit
Wenn man etwas baut, das länger als 6 Monate gepflegt werden soll, stellt sich nicht die Frage ob man umfassende Tests schreibt. Sondern wie man die Gewohnheit aufbaut, sie zu schreiben.
Mit der 5-Test-Regel anfangen. Den Vertrag testen, nicht die Implementierung. An der Grenze mocken. Es so gestalten, dass sich Liefern ohne Tests falsch anfühlt.
Neun Monate und 9.200 Tests später: Es ist die Gewohnheit, über die ich am frohsten bin.
ZenAI ist Open Source auf github.com/Alexander-Bering/KI-AB. ZenBrain, das extrahierte Memory-System, ist auf npm als @zensation/algorithms und @zensation/core.