Lektion 5.1

Tests, Tests, Tests: TSC, Linting, Vitest, Playwright und CI/CD

Dein Projekt mit Type Checks, Linting, Unit- und End-to-End-Tests schützen, verdrahtet in CI/CD

28 minQualität, Sicherheit und das agent-first BusinessVerfügbar

Was du lernst

  • Warum Agents Tests wichtiger machen, nicht unwichtiger, und das vierschichtige Quality Gate
  • Vitest-Unit-Tests und Playwright-End-to-End-Tests schreiben, die Refactors überleben
  • Die ganze Suite automatisch mit einem Pre-Push-Hook und GitHub Actions laufen lassen

Überblick

Es gibt den Mythos, dass die KI den Code schreibt und du deshalb keine Tests mehr brauchst. Das Gegenteil ist wahr. Ein Agent schreibt fröhlich eine funktionierende Funktion um 2 Uhr nachts neu, weil du um eine unzusammenhängende Änderung gebeten hast, und er hat keine Erinnerung daran, warum das alte Verhalten wichtig war. Tests sind, wie du künftigen Agents sagst, was nicht brechen darf. Diese Lektion baut das vierschichtige Quality Gate - Typen, Lint, Unit, End-to-End - und verdrahtet es in einen Pre-Push-Hook und eine CI-Pipeline, sodass Qualität von der Maschine erzwungen wird, nicht von deiner Disziplin.

Was du lernst

Du lernst, was jede Schicht des Quality-Stacks fängt, wie du einen Vitest-Unit-Test und einen Playwright-End-to-End-Test schreibst, die nach einem Refactor weiter funktionieren, wie du einen schlechten Push mit einem Git-Hook blockierst und wie du die ganze Suite automatisch bei jedem Push mit GitHub Actions laufen lässt. Am Ende kannst du einem Agent freie Hand in einem Repo geben und dem Gate vertrauen, seine Fehler zu fangen.

Voraussetzungen

Ein funktionierendes TypeScript-Projekt aus früheren Kursen und die Hooks-Lektion aus Kurs 2, da CI/CD nur die teamweite Version eines Pre-Push-Gates ist. Du musst kein Test-Experte sein. Der Sinn dieser Lektion ist, Testen zu einer Gewohnheit zu machen, die dein Agent für dich erledigt, keine Disziplin, an die du dich erinnern musst.

Das Problem

Wenn du einen Agent hart fährst, fasst er schnell viel Code an. Eine Änderung an einer Datei bricht still eine andere. Der Agent meldet Erfolg, weil die Datei, die er bearbeitet hat, richtig aussieht, und du entdeckst die Regression erst, wenn eine Seite in Produktion leer ist. Manuelles Testen skaliert nicht auf Agent-Tempo - du kannst nicht nach jeder Änderung durch jeden Flow klicken. Ohne ein automatisches Gate ist jede Agent-Session ein kleines Glücksspiel mit deinem Live-Produkt. Die Lösung ist, "funktioniert es noch?" zu einer Frage zu machen, die die Maschine in Sekunden beantwortet.

Das vierschichtige Quality Gate

Jede Schicht fängt eine andere Klasse von Bugs, und sie werden langsamer und gründlicher, je weiter du nach unten gehst. Fahr die schnellen ständig und die langsamen vor dem Ausliefern. Zusammen bilden sie ein Netz, das dicht genug ist, dass schnelle, agent-getriebene Änderungen sicher bleiben.

  • Type Check (tsc --noEmit): fängt Formfehler - eine Funktion, die mit den falschen Argumenten aufgerufen wird, eine Eigenschaft, die nicht existiert, ein null, das du zu behandeln vergessen hast. Sofort und kostenlos.
  • Lint (ESLint): fängt fehleranfällige Muster und Stil-Drift - ungenutzte Variablen, fehlende awaits, versehentliche console.logs. Hält agent-geschriebenen Code konsistent mit deinem.
  • Unit-Tests (Vitest): prüfen, dass deine Logik tatsächlich korrekt ist - eine Preisberechnung, eine Validierungsregel, ein Datumsformatierer. Schnell, bei jedem Speichern laufen lassen.
  • End-to-End-Tests (Playwright): fahren einen echten Browser durch ganze User-Journeys - anmelden, in den Warenkorb legen, auschecken. Langsam, aber sie beweisen, dass das echte Ding funktioniert.

Einen Vitest-Unit-Test schreiben

Unit-Tests sind klein und schnell. Du gibst einer Funktion einen Input und prüfst den Output. Der Trick, der sie Agent-Refactors überleben lässt: teste Verhalten, nicht Implementierung. Prüfe, was die Funktion für eine Nutzerin erzeugen soll, nie die privaten Schritte, die sie dafür geht. Dann kann der Agent die Interna frei umschreiben und der Test bewacht trotzdem den Vertrag.

import { describe, it, expect } from 'vitest'
import { yearlyPricePerMonth } from './pricing'

describe('yearlyPricePerMonth', () => {
  it('shows the discounted monthly figure for a yearly plan', () => {
    // Monthly is 17% above yearly, so yearly-per-month is monthly / 1.17.
    expect(yearlyPricePerMonth({ monthly: 117 })).toBe(100)
  })

  it('never returns a negative price', () => {
    expect(yearlyPricePerMonth({ monthly: 0 })).toBeGreaterThanOrEqual(0)
  })
})
Ein Vitest-Test, der Verhalten festnagelt, nicht Implementierung - lass ihn mit bun run test laufen

Wenn du einen Agent bittest, ein Feature hinzuzufügen, füge deinem Spec eine Zeile hinzu: "schreib einen Vitest-Test dafür". Jetzt hinterlässt der Agent einen Stolperdraht, der das Feature vor dem nächsten Agent schützt, inklusive einer künftigen Version seiner selbst.

Einen Playwright-End-to-End-Test schreiben

Playwright startet einen echten Browser, besucht deine App und klickt sich wie eine Nutzerin durch. Es fängt die Bugs, die Unit-Tests nicht können: eine kaputte Route, einen Button, der nichts tut, ein Formular, das nie absendet. Schreib End-to-End-Tests zuerst für deine Geld-Pfade - die Handvoll Flows, bei denen ein Bruch dich einen Kunden kostet. Wähle Elemente nach dem, was die Nutzerin sieht, oder nach stabilen Test-IDs, nie nach brüchigem CSS, sodass ein Redesign nicht jeden Test bricht.

import { test, expect } from '@playwright/test'

test('a visitor can reach the pricing page from the hero CTA', async ({
  page,
}) => {
  await page.goto('/')
  await page.getByRole('link', { name: 'See pricing' }).click()
  await expect(page).toHaveURL(/\/pricing/)
  await expect(
    page.getByRole('heading', { name: 'Pricing' }),
  ).toBeVisible()
})
Ein Playwright-Test für eine echte User-Journey - lass ihn mit bun run test:e2e laufen

Der Pre-Push-Hook: dein lokales Gate

Der erste Ort, das Gate zu erzwingen, ist deine eigene Maschine, im Moment bevor Code sie verlässt. Ein Git-Pre-Push-Hook fährt deine Prüfungen und verweigert den Push, wenn eine scheitert, sodass kaputter Code nie das Repo erreicht. Das ist dieselbe Idee, die du in Kurs 2 getroffen hast, jetzt auf die volle Suite gerichtet. Halt den lokalen Hook schnell - Typen, Lint und Unit-Tests - und lass die langsamere End-to-End-Suite in CI laufen.

#!/usr/bin/env sh
# .husky/pre-push - blockiert den Push, wenn eine Pruefung scheitert

bun run typecheck || exit 1
bun run lint || exit 1
bun run test || exit 1

echo "All checks passed - pushing."
Ein Pre-Push-Hook, der die schnellen Schichten fährt, bevor Code deine Maschine verlässt

Ein Hook, den du mit --no-verify überspringen kannst, ist ein Vorschlag, kein Gate. Der Hook ist dein schnelles Feedback; CI ist das Gate, das niemand umgehen kann. Du willst beide.

GitHub Actions: das Gate, das niemand überspringen kann

Continuous Integration fährt deine ganze Suite auf GitHubs Servern bei jedem Push und Pull Request, egal, was jemand lokal gefahren hat. Eine Workflow-Datei in .github/workflows sagt GitHub, was zu tun ist. Das Beispiel unten installiert Abhängigkeiten und fährt dann alle vier Schichten inklusive der End-to-End-Tests. Setz deinen Branch so, dass diese Prüfung bestehen muss, bevor irgendetwas merget, und eine Regression kann deinen Main-Branch schlicht nicht erreichen.

name: Quality gate

on:
  push:
    branches: [main]
  pull_request:

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install --frozen-lockfile
      - run: bun run typecheck
      - run: bun run lint
      - run: bun run test
      - run: bunx playwright install --with-deps
      - run: bun run test:e2e
.github/workflows/quality.yml - das volle Gate, automatisch bei jedem Push gefahren

Typische Fehler

Die grossen: null Tests schreiben, weil "der Agent hat es schon geprüft" (er kann deine Live-App nicht sehen); Implementierungsdetails testen, sodass jeder Refactor die Suite rot färbt und du anfängst, sie zu ignorieren; den lokalen Hook so langsam machen, dass du ihn mit --no-verify umgehst; und CI haben, aber nicht verlangen, dass es vor dem Merge besteht, sodass es dekorativ wird. Teste Verhalten, halt das lokale Gate schnell und mach CI verpflichtend.

Business-ROI

Eine Regression, die einen Kunden erreicht, kostet dich Vertrauen, ein Support-Ticket und einen Notfall-Fix, oft zum denkbar schlechtesten Zeitpunkt. Ein Test, der sie fängt, kostet die paar Sekunden, die er zum Laufen braucht. Sobald das Gate existiert, kannst du Agents weit aggressiver arbeiten lassen, weil die Kosten eines Fehlers von "hat Produktion zerbrochen" auf "die Pipeline wurde rot, behebe es vor dem Merge" fallen. Das ist die echte Befreiung: Tests sind kein Overhead auf agentischer Entwicklung, sie sind, was aggressive agentische Entwicklung sicher macht.

Checkliste

Du bist bereit weiterzugehen, wenn jedes davon für ein echtes Projekt stimmt, nicht nur in der Theorie.

  • Du kannst erklären, was jede der vier Schichten fängt, das die anderen verpassen.
  • Mindestens ein Vitest-Test und ein Playwright-Test bestehen in deinem Projekt.
  • Ein Pre-Push-Hook fährt die schnellen Schichten und blockiert einen scheiternden Push.
  • Ein GitHub-Actions-Workflow fährt die volle Suite und ist vor dem Merge erforderlich.

Ressourcen

Setz die offiziellen Vitest- und Playwright-Docs für Matcher und Selektoren als Lesezeichen und die GitHub-Actions-Docs für Workflow-Syntax, da diese sich entwickeln. Füge "schreib einen Test dafür" zu deinen Standard-Spec-Sheet-Constraints hinzu, sodass jede Agent-Aufgabe einen Stolperdraht hinterlässt. Die Hooks-Lektion in Kurs 2 ist eine erneute Lektüre wert, jetzt, wo du die volle Suite verdrahtest.

Deine Aufgabe

Füge einem echten Projekt einen Vitest-Test und einen Playwright-Test hinzu, dann füge den Pre-Push-Hook und den GitHub-Actions-Workflow von oben hinzu. Brich bewusst etwas, das ein Agent brechen könnte - benenne eine Route um, ändere den Output einer Funktion - und bestätige, dass das Gate rot wird. Dieses Rot ist der ganze Sinn: es hat den Fehler gefangen, bevor ein Nutzer es tat.

Nächste Lektion

Qualität abgedeckt, härtet die nächste Lektion Security: Rate Limits auf jedem öffentlichen Endpunkt, CSP-Header, gespeicherte Credentials verschlüsseln und das Pre-Public-Audit, das verhindert, dass ein Secret leakt, wenn du ein Repo auf öffentlich schaltest.

Kommentare

Kommentare werden geladen.

Kommentar schreiben
KommentareWeiter
Nächster Schritt

Bereit, KI als Workflow zu nutzen?

Starte mit dem Starter-Pfad, speichere deinen Fortschritt lokal und synchronisiere alles später kostenlos mit deinem Konto.