Communitygithub.com

python-testing

Python-Teststrategien mit pytest, TDD-Methodik, Fixtures, Mocking, Parametrisierung und Coverage-Anforderungen. Verwenden Sie dies beim Schreiben von pytest-Tests – Fixtures, Mocks, Parametrisierung oder Coverage.

Was ist python-testing?

Dieses Skill bietet eine umfassende Anleitung für das Testen von Python-Anwendungen mit pytest, TDD-Methodik und bewährten Verfahren. Es ist ideal für Entwickler, die qualitativ hochwertige Tests schreiben möchten und unterstützt plattformunabhängig jede Python-Entwicklungsumgebung. Der Skill beschreibt den TDD-Zyklus (Rot, Grün, Refaktorieren), Coverage-Anforderungen von mindestens 80 % und 100 % für kritische Pfade, sowie die grundlegende Verwendung von pytest. Er behandelt ausführlich Fixtures mit verschiedenen Scopes (Funktion, Modul, Session), Parametrisierung, benutzerdefinierte Marker und Testauswahl. Ein großer Abschnitt widmet sich dem Mocking und Patchen mit `unittest.mock`, einschließlich Return-Werten, Exceptions und Autospec. Auch das Testen von asynchronem Code mit pytest-asyncio, das Testen von Exceptions und Seiteneffekten (Dateioperationen) sowie die Organisation von Tests in einer klaren Verzeichnisstruktur werden erklärt. Der Inhalt ist praxisnah mit vielen Codebeispielen und deckt fortgeschrittene Themen wie konfigurierte Fixtures in conftest.py, Mock-Properties und das Testen von Kontext-Managern ab.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/python-testing

In Ihrer bevorzugten KI fragen

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

Dokumentation

Python-Testmuster

Umfassende Teststrategien für Python-Anwendungen mit pytest, TDD-Methodik und bewährten Verfahren.

Wann aktivieren

  • Neuen Python-Code schreiben (TDD befolgen: Rot, Grün, Refaktorieren)
  • Testsuiten für Python-Projekte entwerfen
  • Python-Testabdeckung überprüfen
  • Testinfrastruktur einrichten

Kern-Philosophie des Testens

Testgetriebene Entwicklung (TDD)

Folgen Sie stets dem TDD-Zyklus:

  1. ROT: Schreiben Sie einen fehlschlagenden Test für das gewünschte Verhalten
  2. GRÜN: Schreiben Sie minimalen Code, um den Test zu bestehen
  3. REFACTOR: Verbessern Sie den Code, während die Tests grün bleiben
# Schritt 1: Fehlschlagenden Test schreiben (ROT)
def test_add_numbers():
    result = add(2, 3)
    assert result == 5

# Schritt 2: Minimale Implementierung schreiben (GRÜN)
def add(a, b):
    return a + b

# Schritt 3: Bei Bedarf refaktorieren (REFACTOR)

Coverage-Anforderungen

  • Ziel: 80 %+ Codeabdeckung
  • Kritische Pfade: 100 % Abdeckung erforderlich
  • Verwenden Sie pytest --cov zur Messung der Abdeckung
pytest --cov=mypackage --cov-report=term-missing --cov-report=html

pytest-Grundlagen

Grundlegende Teststruktur

import pytest

def test_addition():
    """Test einfache Addition."""
    assert 2 + 2 == 4

def test_string_uppercase():
    """Test Großschreibung von Zeichenketten."""
    text = "hello"
    assert text.upper() == "HELLO"

def test_list_append():
    """Test Anhängen an Liste."""
    items = [1, 2, 3]
    items.append(4)
    assert 4 in items
    assert len(items) == 4

Assertions

# Gleichheit
assert result == expected

# Ungleichheit
assert result != unexpected

# Wahrheitswert
assert result  # Wahr
assert not result  # Falsch
assert result is True  # Exakt True
assert result is False  # Exakt False
assert result is None  # Exakt None

# Mitgliedschaft
assert item in collection
assert item not in collection

# Vergleiche
assert result > 0
assert 0 <= result <= 100

# Typüberprüfung
assert isinstance(result, str)

# Ausnahmetests (bevorzugter Ansatz)
with pytest.raises(ValueError):
    raise ValueError("Fehlermeldung")

# Ausnahmemeldung überprüfen
with pytest.raises(ValueError, match="ungültige Eingabe"):
    raise ValueError("ungültige Eingabe bereitgestellt")

# Ausnahmeattribute überprüfen
with pytest.raises(ValueError) as exc_info:
    raise ValueError("Fehlermeldung")
assert str(exc_info.value) == "Fehlermeldung"

Fixtures

Grundlegende Fixture-Nutzung

import pytest

@pytest.fixture
def sample_data():
    """Fixture, die Beispieldaten bereitstellt."""
    return {"name": "Alice", "age": 30}

def test_sample_data(sample_data):
    """Test mit der Fixture."""
    assert sample_data["name"] == "Alice"
    assert sample_data["age"] == 30

Fixture mit Setup/Teardown

@pytest.fixture
def database():
    """Fixture mit Setup und Teardown."""
    # Setup
    db = Database(":memory:")
    db.create_tables()
    db.insert_test_data()

    yield db  # Dem Test bereitstellen

    # Teardown
    db.close()

def test_database_query(database):
    """Test Datenbankoperationen."""
    result = database.query("SELECT * FROM users")
    assert len(result) > 0

Fixture-Scopes

# Funktions-Scope (Standard) – wird für jeden Test ausgeführt
@pytest.fixture
def temp_file():
    with open("temp.txt", "w") as f:
        yield f
    os.remove("temp.txt")

# Modul-Scope – wird einmal pro Modul ausgeführt
@pytest.fixture(scope="module")
def module_db():
    db = Database(":memory:")
    db.create_tables()
    yield db
    db.close()

# Session-Scope – wird einmal pro Testsession ausgeführt
@pytest.fixture(scope="session")
def shared_resource():
    resource = ExpensiveResource()
    yield resource
    resource.cleanup()

Fixture mit Parametern

@pytest.fixture(params=[1, 2, 3])
def number(request):
    """Parametrisierte Fixture."""
    return request.param

def test_numbers(number):
    """Test wird 3 Mal ausgeführt, einmal pro Parameter."""
    assert number > 0

Mehrere Fixtures verwenden

@pytest.fixture
def user():
    return User(id=1, name="Alice")

@pytest.fixture
def admin():
    return User(id=2, name="Admin", role="admin")

def test_user_admin_interaction(user, admin):
    """Test mit mehreren Fixtures."""
    assert admin.can_manage(user)

Autouse-Fixtures

@pytest.fixture(autouse=True)
def reset_config():
    """Wird automatisch vor jedem Test ausgeführt."""
    Config.reset()
    yield
    Config.cleanup()

def test_without_fixture_call():
    # reset_config wird automatisch ausgeführt
    assert Config.get_setting("debug") is False

Conftest.py für gemeinsam genutzte Fixtures

# tests/conftest.py
import pytest

@pytest.fixture
def client():
    """Gemeinsam genutzte Fixture für alle Tests."""
    app = create_app(testing=True)
    with app.test_client() as client:
        yield client

@pytest.fixture
def auth_headers(client):
    """Auth-Header für API-Tests generieren."""
    response = client.post("/api/login", json={
        "username": "test",
        "password": "test"
    })
    token = response.json["token"]
    return {"Authorization": f"Bearer {token}"}

Parametrisierung

Grundlegende Parametrisierung

@pytest.mark.parametrize("input,expected", [
    ("hello", "HELLO"),
    ("world", "WORLD"),
    ("PyThOn", "PYTHON"),
])
def test_uppercase(input, expected):
    """Test wird 3 Mal mit verschiedenen Eingaben ausgeführt."""
    assert input.upper() == expected

Mehrere Parameter

@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (0, 0, 0),
    (-1, 1, 0),
    (100, 200, 300),
])
def test_add(a, b, expected):
    """Test Addition mit mehreren Eingaben."""
    assert add(a, b) == expected

Parametrisieren mit IDs

@pytest.mark.parametrize("input,expected", [
    ("[email protected]", True),
    ("invalid", False),
    ("@no-domain.com", False),
], ids=["gültige-email", "fehlendes-at", "fehlende-domain"])
def test_email_validation(input, expected):
    """Test E-Mail-Validierung mit lesbaren Test-IDs."""
    assert is_valid_email(input) is expected

Parametrisierte Fixtures

@pytest.fixture(params=["sqlite", "postgresql", "mysql"])
def db(request):
    """Gegen mehrere Datenbank-Backends testen."""
    if request.param == "sqlite":
        return Database(":memory:")
    elif request.param == "postgresql":
        return Database("postgresql://localhost/test")
    elif request.param == "mysql":
        return Database("mysql://localhost/test")

def test_database_operations(db):
    """Test wird 3 Mal ausgeführt, einmal pro Datenbank."""
    result = db.query("SELECT 1")
    assert result is not None

Marker und Testauswahl

Benutzerdefinierte Marker

# Langsame Tests markieren
@pytest.mark.slow
def test_slow_operation():
    time.sleep(5)

# Integrationstests markieren
@pytest.mark.integration
def test_api_integration():
    response = requests.get("https://api.example.com")
    assert response.status_code == 200

# Unit-Tests markieren
@pytest.mark.unit
def test_unit_logic():
    assert calculate(2, 3) == 5

Bestimmte Tests ausführen

# Nur schnelle Tests ausführen
pytest -m "not slow"

# Nur Integrationstests ausführen
pytest -m integration

# Integrations- oder langsame Tests ausführen
pytest -m "integration or slow"

# Tests ausführen, die als unit markiert sind, aber nicht langsam
pytest -m "unit and not slow"

Marker in pytest.ini konfigurieren

[pytest]
markers =
    slow: marks tests as slow
    integration: marks tests as integration tests
    unit: marks tests as unit tests
    django: marks tests as requiring Django

Mocking und Patchen

Funktionen mocken

from unittest.mock import patch, Mock

@patch("mypackage.external_api_call")
def test_with_mock(api_call_mock):
    """Test mit gemocktem externem API-Aufruf."""
    api_call_mock.return_value = {"status": "success"}

    result = my_function()

    api_call_mock.assert_called_once()
    assert result["status"] == "success"

Rückgabewerte mocken

@patch("mypackage.Database.connect")
def test_database_connection(connect_mock):
    """Test mit gemockter Datenbankverbindung."""
    connect_mock.return_value = MockConnection()

    db = Database()
    db.connect()

    connect_mock.assert_called_once_with("localhost")

Ausnahmen mocken

@patch("mypackage.api_call")
def test_api_error_handling(api_call_mock):
    """Test Fehlerbehandlung mit gemockter Ausnahme."""
    api_call_mock.side_effect = ConnectionError("Netzwerkfehler")

    with pytest.raises(ConnectionError):
        api_call()

    api_call_mock.assert_called_once()

Kontextmanager mocken

@patch("builtins.open", new_callable=mock_open)
def test_file_reading(mock_file):
    """Test Dateilesen mit gemocktem open."""
    mock_file.return_value.read.return_value = "Dateiinhalt"

    result = read_file("test.txt")

    mock_file.assert_called_once_with("test.txt", "r")
    assert result == "Dateiinhalt"

Autospec verwenden

@patch("mypackage.DBConnection", autospec=True)
def test_autospec(db_mock):
    """Test mit Autospec, um API-Missbrauch zu erkennen."""
    db = db_mock.return_value
    db.query("SELECT * FROM users")

    # Dies würde fehlschlagen, wenn DBConnection keine query-Methode hat
    db_mock.assert_called_once()

Klasseninstanzen mocken

class TestUserService:
    @patch("mypackage.UserRepository")
    def test_create_user(self, repo_mock):
        """Test Benutzererstellung mit gemocktem Repository."""
        repo_mock.return_value.save.return_value = User(id=1, name="Alice")

        service = UserService(repo_mock.return_value)
        user = service.create_user(name="Alice")

        assert user.name == "Alice"
        repo_mock.return_value.save.assert_called_once()

Eigenschaft mocken

@pytest.fixture
def mock_config():
    """Erstelle einen Mock mit einer Eigenschaft."""
    config = Mock()
    type(config).debug = PropertyMock(return_value=True)
    type(config).api_key = PropertyMock(return_value="test-key")
    return config

def test_with_mock_config(mock_config):
    """Test mit gemockten Konfigurationseigenschaften."""
    assert mock_config.debug is True
    assert mock_config.api_key == "test-key"

Asynchronen Code testen

Asynchrone Tests mit pytest-asyncio

import pytest

@pytest.mark.asyncio
async def test_async_function():
    """Test asynchrone Funktion."""
    result = await async_add(2, 3)
    assert result == 5

@pytest.mark.asyncio
async def test_async_with_fixture(async_client):
    """Test asynchron mit asynchroner Fixture."""
    response = await async_client.get("/api/users")
    assert response.status_code == 200

Asynchrone Fixture

@pytest.fixture
async def async_client():
    """Asynchrone Fixture, die einen asynchronen Testclient bereitstellt."""
    app = create_app()
    async with app.test_client() as client:
        yield client

@pytest.mark.asyncio
async def test_api_endpoint(async_client):
    """Test mit asynchroner Fixture."""
    response = await async_client.get("/api/data")
    assert response.status_code == 200

Asynchrone Funktionen mocken

@pytest.mark.asyncio
@patch("mypackage.async_api_call")
async def test_async_mock(api_call_mock):
    """Test asynchrone Funktion mit Mock."""
    api_call_mock.return_value = {"status": "ok"}

    result = await my_async_function()

    api_call_mock.assert_awaited_once()
    assert result["status"] == "ok"

Ausnahmen testen

Erwartete Ausnahmen testen

def test_divide_by_zero():
    """Test, dass Division durch Null einen ZeroDivisionError auslöst."""
    with pytest.raises(ZeroDivisionError):
        divide(10, 0)

def test_custom_exception():
    """Test benutzerdefinierte Ausnahme mit Meldung."""
    with pytest.raises(ValueError, match="ungültige Eingabe"):
        validate_input("ungültig")

Ausnahmeattribute testen

def test_exception_with_details():
    """Test Ausnahme mit benutzerdefinierten Attributen."""
    with pytest.raises(CustomError) as exc_info:
        raise CustomError("Fehler", code=400)

    assert exc_info.value.code == 400
    assert "Fehler" in str(exc_info.value)

Seiteneffekte testen

Dateioperationen testen

import tempfile
import os

def test_file_processing():
    """Test Dateiverarbeitung mit temporärer Datei."""
    with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as f:
        f.write("Testinhalt")
        temp_path = f.name

    try:
        result = process_file(temp_path)
        assert result == "verarbeitet: Testinhalt"
    finally:
        os.unlink(temp_path)

Test mit pytest' tmp_path-Fixture

def test_with_tmp_path(tmp_path):
    """Test mit pytest' eingebauter temporärer Pfad-Fixture."""
    test_file = tmp_path / "test.txt"
    test_file.write_text("hallo welt")

    result = process_file(str(test_file))
    assert result == "hallo welt"
    # tmp_path wird automatisch bereinigt

Test mit tmpdir-Fixture

def test_with_tmpdir(tmpdir):
    """Test mit pytest' tmpdir-Fixture."""
    test_file = tmpdir.join("test.txt")
    test_file.write("daten")

    result = process_file(str(test_file))
    assert result == "daten"

Testorganisation

Verzeichnisstruktur

tests/
├── conftest.py                 # Gemeinsam genutzte Fixtures
├── __init__.py
├── unit/                       # Unit-Tests
│   ├── __init__.py
│   ├── test_models.py
│   ├── test_utils.py
│   └── test_services.py
├── integration/                # Integrationstests
│   ├── __init__.py
│   ├── test_api.py
│   └── test_database.py
└── e2e/                        # End-to-End-Tests
    ├── __init__.py
    └── test_user_flow.py

Testklassen

class TestUserService:
    """Gruppiere verwandte Tests in einer Klasse."""

    @pytest.fixture(autouse=True)
    def setup(self):
        """Setup wird vor jedem Test in dieser Klasse ausgeführt."""
        self.service = UserService()

    def test_create_user(self):
        """Test Benutzererstellung."""
        user = self.service.create_user("Alice")
        assert user.name == "Alice"

    def test_delete_user(self):
        """Test Benutzerlöschung."""
        self.service.create_user("Alice")
        assert self.service.delete_user("Alice") is True
        assert self.service.get_user("Alice") is None

    def test_duplicate_user(self):
        """Test doppelter Benutzer."""
        self.service.create_user("Alice")
        with pytest.raises(DuplicateUserError):
            self.service.create_user("Alice")

Konfiguration mit pytest.ini

[pytest]
minversion = 7.0
python_files = tests/*.py test_*.py
python_classes = Test*
python_functions = test_*
testpaths = tests

# Coverage-Einstellungen
addopts = --strict-markers --strict-config

# Marker-Definitionen
markers =
    slow: langsamer Test (dauert länger als 1 Sekunde)
    integration: Integrationstest
    unit: Unit-Test
    asyncio: asynchroner Test
    smoke: Rauchtest für kritische Pfade

Spezifische Tests ausführen

# Alle Tests ausführen
pytest

# Ausführliche Ausgabe
pytest -v

# Tests mit bestimmten Namen ausführen
pytest -k "test_user"

# Tests mit bestimmten Markern ausführen
pytest -m "unit"

# Tests ausschließen
pytest -m "not slow"

# Im Abbruchmodus beim ersten Fehlschlag ausführen
pytest -x

# Letzten Fehlschlag zuerst ausführen
pytest --ff

# Tests parallel ausführen (benötigt pytest-xdist)
pytest -n auto

# Mit Coverage ausführen
pytest --cov=mypackage --cov-report=html

Individual skills in this repo

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

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

Verwandte Skills