Claude Code Pytest: Tests schreiben, Fixtures aufsetzen, Coverage messen

Testing gehört zu den Aufgaben, die Entwickler am häufigsten aufschieben — nicht weil sie unwichtig wären, sondern weil das Aufsetzen einer sauberen Test-Infrastruktur Zeit kostet. Fixtures einrichten, Mocks konfigurieren, Coverage-Reports interpretieren: All das kommt on top, wenn eigentlich schon die eigentliche Feature-Arbeit wartet.

Claude Code Pytest verändert diese Gleichung. Statt Boilerplate von Hand zu schreiben, beschreibst du was du testen willst — und Claude Code generiert die Test-Datei, richtet die Fixtures ein und findet Edge-Cases, die du vielleicht übersehen hättest. Dieser Artikel zeigt, wie das konkret funktioniert.

Claude Code Mastery — Testing, Agents, Hooks auf Deutsch

Nicht nur Pytest: der Kurs zeigt, wie du Claude Code wirklich produktiv einsetzt — von automatisierten Tests über Debugging bis zu autonomen Workflows. Einmalig bezahlt, kein Abo.

Zum Kurs — Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

1. Was ist Pytest — und warum es der Standard ist

Pytest ist Pythons meistgenutztes Testing-Framework. Im Vergleich zum eingebauten unittest-Modul punktet es vor allem durch seine klare Syntax: kein Erben von Testklassen, keine umständlichen self.assert*-Methoden — stattdessen einfache assert-Statements, die Python-nativ sind.

# Einfachster pytest-Test
def test_addition():
    assert 1 + 1 == 2

def test_string_upper():
    assert "hallo".upper() == "HALLO"

Pytest erkennt alle Dateien, die mit test_ beginnen oder auf _test.py enden, und alle Funktionen darin, die mit test_ starten. Kein Setup, keine Konfiguration für den Einstieg. Das macht es zum bevorzugten Werkzeug für Python-Projekte jeder Größe — von kleinen Skripten bis zu Django-Anwendungen.

Installation: pip install pytest reicht für den Start. Für Coverage kommt pip install pytest-cov dazu, für Mocking-Unterstützung pip install pytest-mock.

2. Teststruktur: Dateien, assert-Statements, Testklassen

Eine saubere Teststruktur erleichtert die Navigation und verhindert, dass Tests sich gegenseitig beeinflussen. Die Konvention: ein tests/-Verzeichnis auf der gleichen Ebene wie der Quellcode, mit Dateien die die Struktur des Projekts spiegeln.

mein-projekt/
  src/
    rechner.py
    api/
      users.py
  tests/
    test_rechner.py
    api/
      test_users.py

Testklassen sind optional — sie bieten sich an, wenn mehrere Tests denselben Kontext teilen:

class TestRechner:
    def test_addition(self):
        assert rechner.addiere(2, 3) == 5

    def test_division_durch_null(self):
        with pytest.raises(ZeroDivisionError):
            rechner.dividiere(10, 0)

Das pytest.raises-Kontextmanager ist besonders wertvoll: Er testet nicht nur, ob eine Exception geworfen wird, sondern auch ob es die richtige ist. Ein fehlendes raises-Test ist einer der häufigsten Lücken in Python-Test-Suites.

3. Fixtures: @pytest.fixture und conftest.py

Fixtures sind der Kern von Pytest. Sie liefern Testdaten, Datenbankverbindungen, temporäre Dateien oder andere Abhängigkeiten — und stellen sicher, dass jeder Test in einem definierten Zustand startet.

import pytest

@pytest.fixture
def beispiel_nutzer():
    return {"id": 1, "name": "Anna", "email": "anna@beispiel.de"}

def test_nutzer_name(beispiel_nutzer):
    assert beispiel_nutzer["name"] == "Anna"

Der scope-Parameter steuert, wie lange eine Fixture lebt:

@pytest.fixture(scope="session")
def db_verbindung():
    conn = datenbank.verbinden("sqlite:///:memory:")
    datenbank.schema_erstellen(conn)
    yield conn
    conn.schliessen()

Das yield-Muster ist wichtig: alles vor yield ist Setup, alles danach ist Teardown. Pytest führt den Teardown auch dann aus, wenn ein Test fehlschlägt.

Fixtures die in mehreren Dateien gebraucht werden, gehören in conftest.py — eine spezielle Datei, die Pytest automatisch lädt:

# tests/conftest.py
@pytest.fixture(scope="session")
def app_config():
    return {"debug": False, "testing": True, "datenbank": "sqlite:///:memory:"}

4. Parametrisierung: @pytest.mark.parametrize

Statt denselben Test zehnmal mit verschiedenen Werten zu kopieren, nutzt man parametrize. Das hält die Tests kompakt und macht es einfach, neue Fälle hinzuzufügen:

@pytest.mark.parametrize("eingabe,erwartet", [
    ("hallo", "HALLO"),
    ("welt", "WELT"),
    ("", ""),
    ("123abc", "123ABC"),
])
def test_grossschreibung(eingabe, erwartet):
    assert eingabe.upper() == erwartet

Pytest zeigt bei Fehlern genau welche Parameterkombination gescheitert ist — das beschleunigt die Fehlersuche erheblich. Für komplexere Fälle lassen sich Parametrize-Dekoratoren stapeln, was eine Matrix von Testfällen erzeugt.

5. Mocking: unittest.mock, pytest-mock und monkeypatch

Tests sollen isoliert laufen — ohne echte Datenbankverbindungen, API-Aufrufe oder Filesystem-Operationen. Dafür gibt es Mocking.

unittest.mock

from unittest.mock import patch, MagicMock

def test_api_aufruf():
    with patch("mein_modul.requests.get") as mock_get:
        mock_get.return_value.json.return_value = {"status": "ok"}
        mock_get.return_value.status_code = 200

        ergebnis = mein_modul.nutzer_laden(1)
        assert ergebnis["status"] == "ok"
        mock_get.assert_called_once()

pytest-mock (empfohlen)

pytest-mock stellt die mocker-Fixture bereit, die sauberer zu schreiben ist:

def test_api_aufruf(mocker):
    mock_get = mocker.patch("mein_modul.requests.get")
    mock_get.return_value.json.return_value = {"status": "ok"}

    ergebnis = mein_modul.nutzer_laden(1)
    assert ergebnis["status"] == "ok"

monkeypatch

Pytest's eingebautes monkeypatch ist ideal für Umgebungsvariablen, Konfigurationswerte oder einfache Attribut-Ersetzungen:

def test_mit_umgebungsvariable(monkeypatch):
    monkeypatch.setenv("API_KEY", "test-schluessel-123")
    monkeypatch.setattr(config, "timeout", 30)

    ergebnis = mein_dienst.initialisieren()
    assert ergebnis.konfiguriert is True

Wichtig beim Mocking: Immer den Pfad mocken, wo das Objekt benutzt wird, nicht wo es definiert ist. Statt patch("requests.get") also patch("mein_modul.requests.get"). Das ist die häufigste Fehlerquelle beim Einstieg in Mocking.

6. Coverage: pytest-cov und Coverage-Gaps finden

Eine hohe Test-Coverage gibt Sicherheit bei Refactorings und macht Bugs sichtbar bevor sie in Production gehen. pytest-cov integriert Coverage-Messung direkt in den pytest-Lauf:

# Coverage für ein Modul messen
pytest --cov=src tests/

# HTML-Report generieren
pytest --cov=src --cov-report=html tests/

# Fehlende Zeilen direkt im Terminal anzeigen
pytest --cov=src --cov-report=term-missing tests/

Der HTML-Report unter htmlcov/index.html zeigt exakt welche Zeilen und Branches noch nicht getestet sind. Besonders wertvoll: die Branch-Coverage zeigt nicht nur ob eine Zeile ausgeführt wurde, sondern ob alle Verzweigungen (beide Pfade eines if/else) abgedeckt sind.

# Branch-Coverage aktivieren
pytest --cov=src --cov-branch --cov-report=term-missing tests/

Eine sinnvolle Mindestgrenze setzen und in CI erzwingen:

# pytest.ini oder pyproject.toml
[tool.pytest.ini_options]
addopts = "--cov=src --cov-fail-under=80"

7. Claude Code Pytest: So beschleunigt Claude Code deinen Test-Workflow

Jetzt kommt der entscheidende Teil: wie Claude Code Pytest-Arbeit konkret verändert.

Tests für bestehenden Code generieren

Der häufigste Anwendungsfall — Legacy-Code hat keine Tests, und man weiß nicht wo anfangen:

claude "Schreib pytest-Tests für src/services/bestellungen.py.
Decke die Hauptpfade ab, Fehlerfälle, und Edge-Cases bei leerer
Bestellliste. Nutze Fixtures für Testdaten."

Claude Code liest die Datei, analysiert alle Methoden und ihre möglichen Zustände, und schreibt eine vollständige Test-Datei mit sinnvollen Testfällen — inklusive Fixtures für die Testdaten und Mocks für externe Abhängigkeiten.

Fixtures für komplexe Setups aufsetzen

claude "Erstelle eine conftest.py mit Fixtures für unsere Django-Tests:
Datenbankverbindung mit session scope, einen authentifizierten Test-Client,
und Beispieldaten für User, Produkte und Bestellungen."

Edge-Cases identifizieren

"Analysiere meine Funktion berechne_rabatt() in pricing.py und liste alle Edge-Cases auf, die ich testen sollte. Dann schreib die Tests dafür mit parametrize."

Claude Code findet Fälle, die man selbst übersieht: negative Werte, Werte genau an Grenzen, leere Listen, None-Werte, sehr große Zahlen, Unicode-Strings. Das Ergebnis ist eine parametrisierte Test-Suite, die auch unerwartete Eingaben abdeckt.

Coverage-Gaps schließen

pytest --cov=src --cov-report=term-missing tests/ 2>&1 | \
  claude "Welche Codepfade sind noch nicht getestet?
  Schreib Tests für die fehlenden Zeilen."

Claude Code liest den Coverage-Report, identifiziert die unbetesteten Pfade und schreibt gezielt Tests dafür — oft inklusive dem richtigen Mocking-Setup, damit auch schwer erreichbare Codepfade (Fehlerbehandlung, Timeouts, Netzwerkfehler) getestet werden können.

Zwei verwandte Artikel, die auf diesem Thema aufbauen:


Claude Code Mastery — von Pytest bis zum produktiven Agenten

Testing ist eine Stärke von Claude Code — aber nicht die einzige. Im Kurs lernst du Agents, MCP-Server, Hooks, Multi-Agent-Workflows und mehr. Vollständig auf Deutsch, einmalig bezahlt.

Jetzt starten → Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht

Kurs · Claude Code Mastery

Von Pytest zum produktiven AI-Agenten

Testing. Debugging. Agents. MCP. Hooks. Alles auf Deutsch, einmalig bezahlt — kein Abo, keine Plattformabhängigkeit.

Jetzt einsteigen → Kursübersicht ansehen →

Einmalzahlung · Kein Abo · 14 Tage Rückgaberecht