Testing mit pytest
Neben dem eingebauten Test-Framework von Django wird in der Praxis häufig pytest verwendet. Es ist schlanker, flexibler und lässt sich gut mit Django kombinieren.
Wir nutzen zusätzlich:
pytest-django(Integration mit Django)factory_boy(Testdaten erzeugen)
Installation
uv add pytest pytest-django factory-boy
Konfiguration
Eine minimale pytest.ini im Projekt-Root:
[pytest]
DJANGO_SETTINGS_MODULE = event_manager.settings.dev
python_files = tests.py test_*.py *_tests.py
Ordnerstruktur
Tests werden strukturiert im tests-Verzeichnis der App abgelegt:
events/
tests/
test_models.py
test_views.py
Grundidee
Tests sind einfache Funktionen mit dem Präfix test_. pytest übernimmt das Setup automatisch.
Wir verwenden deine Factories:
UserFactoryCategoryFactoryEventFactory
Model-Tests
import pytest
from django.core.exceptions import ValidationError
from django.utils import timezone
from datetime import timedelta
from events.models import Category, Event
from events.factories import CategoryFactory, EventFactory, UserFactory
@pytest.mark.django_db
def test_create_category_successful():
category = CategoryFactory()
assert category.id is not None
@pytest.mark.django_db
def test_category_name_unique():
CategoryFactory(name="Sports")
with pytest.raises(Exception):
CategoryFactory(name="Sports")
@pytest.mark.django_db
def test_event_proper_update():
event = EventFactory(name="Old Name")
event.name = "New Name"
event.full_clean()
event.save()
assert Event.objects.get(id=event.pk).name == "New Name"
@pytest.mark.django_db
def test_invalid_event_date_in_past():
event = EventFactory.build(
date=timezone.now() - timedelta(days=1)
)
with pytest.raises(ValidationError):
event.full_clean()
@pytest.mark.django_db
def test_invalid_event_name_too_short():
event = EventFactory.build(name="ab")
with pytest.raises(ValidationError):
event.full_clean()
View-Tests
pytest stellt mit client einen Test-Client bereit.
import pytest
from django.urls import reverse
from events.factories import EventFactory, UserFactory
@pytest.mark.django_db
def test_event_delete_as_author(client):
user = UserFactory()
event = EventFactory(author=user)
client.force_login(user)
url = reverse("events:event-delete", args=[event.slug])
response = client.post(url)
assert response.status_code in (200, 302)
assert not EventFactory._meta.model.objects.filter(id=event.pk).exists()
@pytest.mark.django_db
def test_event_edit_as_unauthorized_user(client):
author = UserFactory()
other_user = UserFactory()
event = EventFactory(author=author)
client.force_login(other_user)
url = reverse("events:event-update", args=[event.slug])
response = client.post(url, {"name": "Hacked Name"})
assert response.status_code in (403, 302)
Starten der Tests
pytest
oder mit mehr Output:
pytest -v
Good practice
Tests sollten schnell und unabhängig voneinander sein. Nutze Factories statt manuell erzeugter Daten und teste gezielt kritische Logik.