Visuelle QA als CI-Pipeline-Stufe

Ich habe letzten Monat einen PR gemergt. Das Code-Review sah gut aus. Tests bestanden. Dann öffnete ich die Seite auf meinem Handy und die Seitenleiste war komplett kaputt.

Die Lösung war trivial—eine fehlende Media Query. Der Bug war offensichtlich, sobald man sich tatsächlich die mobile Ansicht ansah. Niemand tat es.

Also fügte ich eine Pipeline-Stufe hinzu, die hinschaut.

Ich öffne ein GitHub-Issue, das sagt:

Implementiere die Option Manuelle Eingabe zum Hinzufügen von Clients. Beim Klicken öffnet sich ein breites Slide-over-Drawer mit einem Formular zum Erstellen eines neuen Clients.

Einen Commit später landet der PR mit über 30 Screenshots, die beweisen, dass jeder Zustand in jedem Viewport funktioniert. Null manuelles Testen. Der einzige Aufwand war das Schreiben der Feature-Beschreibung.

Das Konzept

Bei jedem Push zu einem PR führt ein GitHub Actions Workflow aus:

  1. Startet einen Docker Compose Stack (Django + Postgres) vom PR-Branch
  2. Führt Migrationen aus und erstellt einen Testbenutzer
  3. Übergibt die lokale URL an Claude Code mit Playwright MCP (headless Chromium)
  4. Testet interaktive Elemente basierend auf dem PR-Diff
  5. Erstellt Screenshots jedes Zustands in drei Viewports
  6. Postet die Ergebnisse als PR-Kommentar

Claude Code steuert den Browser. Docker Compose stellt die App bereit. GitHub Actions verbindet alles. Reviewer sehen Funktionsnachweise, ohne lokal etwas auszuführen.

%%{init: {"flowchart": {"subGraphTitleMargin": {"top": 3, "bottom": 10}}} }%%
graph TD
    A["PR Push"] --> B

    subgraph GHA["GitHub Actions Runner"]
        B["Checkout + Docker Compose"] --> C["Migrationen + Test-Daten"]
        C --> Agent

        subgraph Agent["Claude Code + Playwright"]
            direction LR
            D["PR Diff lesen"] --> E["Dateien → URLs mappen"]
            E --> F["Login + Navigation"]
            F --> G["Screenshot ×3 Viewports"]
        end
    end

    Agent --> H["PR-Kommentar"]

    style GHA fill:transparent,stroke:#888
    style Agent fill:transparent,stroke:#888

Der Workflow selbst ist unkompliziert:

name: UI Screenshots
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  screenshots:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      # ... Docker Compose Setup, Migrationen, Testbenutzer-Erstellung ...

      - name: Start services
        run: |
          docker compose -f docker-compose.local.yml up -d --wait

      - name: Install Playwright
        run: npx playwright install --with-deps chromium

      - name: Run Claude for Screenshots
        uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          claude_args: >-
            --allowed-tools
            "mcp__playwright__*,
            Bash(gh pr diff:*),
            Bash(gh pr comment:*),
            Read,Glob,Grep"
          prompt: |
            You are a visual QA agent. Your job is to visually
            verify a UI deployment.

            PR: ${{ github.event.pull_request.number }}
            The Django app is running at http://localhost:8001

            1. Run `gh pr diff` to find changed templates/views
            2. Map changed files to URLs — only test pages
               affected by this push
            3. Login, then screenshot each page at three viewports:
               1440x900, 1024x768, 640x1136
            4. Save screenshots with descriptive filenames
            5. Post a PR comment with:
               - Each test performed with pass/fail
               - Any visual issues found
               - Total screenshots captured

Die Viewport-Matrix

GerätGrößeWarum
Desktop1440×900Standard-Laptop
Tablet1024×768iPad Hochformat
Mobil640×1136iPhone SE

Jeder interaktive Zustand wird in allen drei erfasst. Eine Seitenleisten-Komponente erzeugt 12+ Screenshots:

Multipliziert mit hellen/dunklen Themes schauen Sie auf 24 Screenshots für eine Komponente. Das ist der Punkt. Erschöpfender visueller Nachweis als CI-Artefakt.

Was Es Tatsächlich Ausgibt

Hier ist echte Ausgabe von einem PR, der eine manuelle Eingabeseite hinzufügte. Der Agent postet einen PR-Kommentar mit einer strukturierten Zusammenfassung, Screenshots in jedem Viewport und einer Test-Checkliste:

Von dem visuellen QA-Agenten erstelltes GitHub-Issue

Unter dieser Zusammenfassung fügt der Agent Screenshots jedes getesteten Zustands hinzu. Hier ist die Desktop-Ansicht—Client-Liste mit Daten, plus geöffnetes Client-Hinzufügen-Drawer:

Desktop-Ansicht: Client-Liste und Client-Hinzufügen-Drawer bei 1440x900

Der Agent testete:

Ich übersprung einzelne Feld-Fokus-Zustände für diesen PR—abnehmende Erträge bei einem einfachen Formular. Der Agent kann es tun, aber 50 Screenshots von Texteingaben, die Fokus erhalten, sind kein nützliches Signal.

Der PR-Kommentar wird zu einem visuellen Changelog. Jeder Testlauf endet mit einer Checkliste:

Testing Performed Checkliste mit allen bestandenen Punkten

In sechs Monaten kann ich jeden PR anschauen und genau sehen, wie die UI aussah, als sie ausgeliefert wurde.

Das Test-Manifest

Der Prompt im obigen Workflow ist generisch — er funktioniert für jeden PR. Die Spezifität kommt vom Diff selbst. Der Agent liest gh pr diff, findet heraus, welche Templates und Views sich geändert haben, mappt diese zu URLs und entscheidet, was zu testen ist.

Für eine Formular-Komponente bedeutet das, er wird testen:

component: ClientForm
states:
  - empty form
  - filled form (valid data)
  - validation error (submit without name)
  - submitting (loading state)
  - success (confirmation)
interactions:
  - submit empty form (trigger validation)
  - fill all fields, submit
  - verify client appears in list after submit

Kein manueller Testplan nötig. Der Agent leitet die Testoberfläche aus den Code-Änderungen ab.

Warum Screenshots in PR-Kommentaren

Inline-Screenshots im PR-Kommentar haben einige Vorteile:

  1. Sichtbar — Reviewer sehen sie ohne zu Artefakten durchzuklicken
  2. Kontextuell — direkt neben dem Diff, den sie reviewen
  3. Vergleichbar — jeder Push aktualisiert den Kommentar, sodass Sie vorher/nachher sehen
  4. Auditierbar — die Kommentarhistorie zeigt, was bei jedem Commit getestet wurde

Was Das Fängt

Echte Bugs in der ersten Woche gefangen:

Jeder davon bestand das Code-Review. Jeder war in den Screenshots offensichtlich.

Die Kosten

Claude mit Playwright MCP in CI auszuführen dauert 2-4 Minuten, abhängig davon, wie viele Zustände getestet werden müssen. Für einen typischen PR, der eine Komponente berührt, sind es etwa 90 Sekunden.

Vergleichen Sie mit: in Produktion deployen und von Benutzern erfahren, dass das mobile Layout kaputt ist. Unbezahlbar.

Die Verschiebung

Visuelle QA war immer der Engpass. Sie können Unit-Tests automatisieren, Integrationstests, sogar End-to-End-Flows — aber jemand muss immer noch auf die UI schauen. Das war jahrzehntelang wahr.

Es ist nicht mehr wahr. Agenten mit Browsern führen nicht nur Skripte aus. Sie interpretieren, navigieren, interagieren und beurteilen. Die Testoberfläche ist nicht hardcodiert — sie wird aus der Änderung abgeleitet. Jeder PR erhält erschöpfende visuelle Abdeckung, die kein manueller Prozess erreichen könnte.

Dies ändert QE von einem Tor zu einer Garantie. Nicht “hat es jemand überprüft”, sondern “die Pipeline hat es überprüft, hier ist der Beweis”. Jeder PR, jeder Push, jeder Viewport. Die QE-Rolle verschwindet nicht. Sie bewegt sich upstream. Anstatt Testpläne auszuführen, definieren Sie, was dem Agenten wichtig sein sollte. Anstatt Bugs zu fangen, entwerfen Sie das System, das sie fängt.