Django cookbook · Beginner
Test views and APIs with pytest-django
Tests are slow, depend on each other, and don't catch query regressions.
Approach
Use pytest-django fixtures for the database and clients, small factory functions for data, and assertions on status codes, response bodies and query counts.
Solution
# conftest.py
import pytest
from rest_framework.test import APIClient
@pytest.fixture
def user(django_user_model):
return django_user_model.objects.create_user(email="[email protected]", password="pw-123456789")
@pytest.fixture
def api(user):
client = APIClient()
client.force_authenticate(user)
return client
# test_notes.py
import pytest
from notes.models import Note
@pytest.mark.django_db
def test_lists_only_own_notes(api, user, django_user_model, django_assert_max_num_queries):
other = django_user_model.objects.create_user(email="[email protected]", password="pw-123456789")
Note.objects.create(owner=user, title="mine")
Note.objects.create(owner=other, title="theirs")
with django_assert_max_num_queries(3):
response = api.get("/api/notes/")
assert response.status_code == 200
assert [n["title"] for n in response.json()["results"]] == ["mine"]
@pytest.mark.django_db
def test_rejects_anonymous(client):
assert client.get("/api/notes/").status_code in (401, 403)
Pitfalls
- Sharing fixtures with module scope that mutate the database between tests.
- Fixtures with huge object graphs; build only what each test needs.
- Not testing permissions and other users' data, the most common API bug.
Variations
factory_boyormodel_bakerygenerate model instances with sensible defaults.- Run with
--reuse-dblocally and-n auto(pytest-xdist) in CI.
Further reading: pytest-django
Reference: https://pytest-django.readthedocs.io/en/latest/
More Django recipes
- Add a required column without downtime
- Add full-text search with PostgreSQL
- Add health checks and structured logging
- Avoid N+1 queries with select_related and prefetch_related
- Cache expensive queries and invalidate on change
- Call external APIs concurrently in async views
- Configure settings from environment variables
- Enforce rules with database constraints